Metadata-Version: 2.1
Name: odoo-addon-web_widget_mapbox
Version: 18.0.1.0.0.2
Requires-Python: >=3.10
Requires-Dist: odoo==18.0.*
Summary: Mapbox map widget
Home-page: https://github.com/OCA/geospatial
License: LGPL-3
Author: Cetmix, Odoo Community Association (OCA)
Author-email: support@odoo-community.org
Classifier: Programming Language :: Python
Classifier: Framework :: Odoo
Classifier: Framework :: Odoo :: 18.0
Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)
Classifier: Development Status :: 4 - Beta
Description-Content-Type: text/x-rst

.. image:: https://odoo-community.org/readme-banner-image
   :target: https://odoo-community.org/get-involved?utm_source=readme
   :alt: Odoo Community Association

=============
Mapbox Widget
=============

.. 
   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
   !! This file is generated by oca-gen-addon-readme !!
   !! changes will be overwritten.                   !!
   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
   !! source digest: sha256:13e6eee8c08f5e884068a5f62a1c6293d605a7a67a712fcc07676769dce66137
   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
    :target: https://odoo-community.org/page/development-status
    :alt: Beta
.. |badge2| image:: https://img.shields.io/badge/license-LGPL--3-blue.png
    :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
    :alt: License: LGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fgeospatial-lightgray.png?logo=github
    :target: https://github.com/OCA/geospatial/tree/18.0/web_widget_mapbox
    :alt: OCA/geospatial
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
    :target: https://translation.odoo-community.org/projects/geospatial-18-0/geospatial-18-0-web_widget_mapbox
    :alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
    :target: https://runboat.odoo-community.org/builds?repo=OCA/geospatial&target_branch=18.0
    :alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

This module adds a form-view Mapbox map widget for JSON fields.

Declare a ``fields.Json`` field on any model and set ``widget="mapbox"``
in the form arch. The widget renders markers from a JSON payload,
supports map and satellite styles, zoom, pan, pitch, and fullscreen, and
can open a popup form when a marker is marked clickable.

Markers may use optional Font Awesome icons, sizes, and HEX colors. When
several markers share a location, earlier items in the payload are drawn
on top. The Map / Satellite control remembers the last choice in the
browser and applies it to every mapbox widget on that origin.

Mapbox GL JS is loaded from the Mapbox CDN at runtime and is not
vendored in this addon.

**Table of contents**

.. contents::
   :local:

Configuration
=============

1. Create a Mapbox account and a **public** (``pk.``) token at
   https://account.mapbox.com/.
2. Open **Settings → General Settings** and find **Mapbox Token** under
   Integrations.
3. Paste the public token and save. Backend forms that use
   ``widget="mapbox"`` then receive the token through the user session.
4. The browser must be able to reach ``api.mapbox.com`` (Mapbox GL JS,
   styles, and tiles).

The token stored here is a Mapbox public token. It is still a
credential: restrict who can change Settings, and do not treat it as
anonymous data. This module does **not** read or write the Enterprise
``web_map`` token (``web_map.token_map_box`` / ``map_box_token``).

Usage
=====

Add a JSON field on the model that should show a map, then use the
widget in the form view:

.. code:: xml

   <field name="my_map" widget="mapbox"/>

The field value must be a JSON **object**. ``null``, ``false``, or a
non-object value show a placeholder instead of a map. Unknown extra keys
are kept when the widget writes.

The map container is full width of the field and **400px** tall. A
missing Mapbox token, a failed CDN load, or empty / non-object data each
show a translated placeholder and do not construct a map.

Payload
-------

.. code:: json

   {
     "elements": [],
     "style": {
       "map": "mapbox://styles/mapbox/streets-v12",
       "satellite": "mapbox://styles/mapbox/satellite-streets-v12"
     },
     "default_center": {"lat": 52.37, "lon": 4.89},
     "default_zoom": 12,
     "allow_zoom": true,
     "allow_pan": true,
     "allow_fullscreen": true,
     "default_pitch": 0,
     "allow_pitch": true,
     "updated": []
   }

+----------------------+-----------------------+--------------------------------------------------+
| Key                  | Type                  | Default if omitted                               |
+======================+=======================+==================================================+
| ``elements``         | array of marker       | ``[]``                                           |
|                      | objects               |                                                  |
+----------------------+-----------------------+--------------------------------------------------+
| ``style.map``        | Mapbox style URL      | ``mapbox://styles/mapbox/streets-v12``           |
+----------------------+-----------------------+--------------------------------------------------+
| ``style.satellite``  | Mapbox style URL      | ``mapbox://styles/mapbox/satellite-streets-v12`` |
+----------------------+-----------------------+--------------------------------------------------+
| ``default_center``   | ``{lat, lon}``        | first valid element, else                        |
|                      |                       | ``{"lat": 0, "lon": 0}``                         |
+----------------------+-----------------------+--------------------------------------------------+
| ``default_zoom``     | number                | ``12``                                           |
+----------------------+-----------------------+--------------------------------------------------+
| ``allow_zoom``       | bool                  | ``true``                                         |
+----------------------+-----------------------+--------------------------------------------------+
| ``allow_pan``        | bool                  | ``true``                                         |
+----------------------+-----------------------+--------------------------------------------------+
| ``allow_fullscreen`` | bool                  | ``true`` (Mapbox fullscreen control)             |
+----------------------+-----------------------+--------------------------------------------------+
| ``default_pitch``    | number                | ``0``, clamped to ``[0, 85]``                    |
+----------------------+-----------------------+--------------------------------------------------+
| ``allow_pitch``      | bool                  | ``true``                                         |
+----------------------+-----------------------+--------------------------------------------------+
| ``updated``          | array                 | ``[]``; written by the widget after a drag       |
+----------------------+-----------------------+--------------------------------------------------+

The map does **not** fit bounds. When ``default_center`` is a valid
``{lat, lon}`` pair it is used even if markers exist. Otherwise the
camera centers on the first valid element.

Classic Mapbox style URLs only — not Mapbox Standard. The initial style
is ``style.map``, unless the browser already stores a Satellite
preference (see below).

Map / Satellite preference
--------------------------

The Map / Satellite control writes the last choice to browser storage
(``web_widget_mapbox.style``: ``map`` or ``satellite``) and applies it
to **every** mapbox widget on this origin, including maps that are
already open. New maps start with that choice. An invalid or missing
value falls back to Map. The preference survives page reloads on the
same origin.

Changing style keeps the current pitch.

Markers
-------

+-------------------+--------+-----------------------------------------+
| Key               | Type   | Notes                                   |
+===================+========+=========================================+
| ``lat`` / ``lon`` | number | required for a visible marker; ``lat``  |
|                   |        | in ``[-90, 90]``, ``lon`` in            |
|                   |        | ``[-180, 180]``                         |
+-------------------+--------+-----------------------------------------+
| ``label``         | string | optional; Mapbox popup when a form is   |
|                   |        | not opened                              |
+-------------------+--------+-----------------------------------------+
| ``rec_model``     | string | model to open on click                  |
+-------------------+--------+-----------------------------------------+
| ``rec_id``        | int    | positive id of ``rec_model``            |
+-------------------+--------+-----------------------------------------+
| ``clickable``     | bool   | default ``false``; with ``rec_model``   |
|                   |        | and ``rec_id`` opens a popup form       |
+-------------------+--------+-----------------------------------------+
| ``icon``          | string | Font Awesome 4.7 name, with or without  |
|                   |        | the ``fa-`` prefix                      |
+-------------------+--------+-----------------------------------------+
| ``size``          | int    | optional size in pixels of the custom   |
|                   |        | ``icon`` element; ignored when ``icon`` |
|                   |        | is omitted                              |
+-------------------+--------+-----------------------------------------+
| ``color``         | string | optional HEX ``#RGB`` or ``#RRGGBB``;   |
|                   |        | tints the default Mapbox pin or the     |
|                   |        | Font Awesome icon                       |
+-------------------+--------+-----------------------------------------+
| ``editable``      | bool   | default ``false``; when true and the    |
|                   |        | field is writable, the marker can be    |
|                   |        | dragged                                 |
+-------------------+--------+-----------------------------------------+
| ``index``         | int    | only on ``updated`` items; 0-based      |
|                   |        | index in ``elements``                   |
+-------------------+--------+-----------------------------------------+

Invalid coordinates are skipped. Every valid element is drawn as an
equal marker. When several markers share a location, earlier items in
``elements`` are drawn on top (so list order controls which pin receives
clicks). Invalid or omitted ``color`` leaves the default Mapbox pin
color, or the icon’s inherited color.

A marker click opens a popup form (``FormViewDialog``) only when
``clickable`` is true **and** ``rec_model`` is a non-empty string
**and** ``rec_id`` is a positive integer. If that triple is incomplete,
the form is not opened (the label popup is used instead when ``label``
is set). A click that follows a drag does not open the form. If the map
is in fullscreen, the click leaves fullscreen first so the form is
visible, then the map is resized to the in-page container.

Drag and ``updated``
--------------------

Dragging does not rewrite ``elements``. On drag end the widget copies
that element, sets ``lat`` / ``lon`` from the marker, sets ``index`` to
the source position, and upserts it into root ``updated``. Extra element
keys (including ``clickable``, ``rec_model``, ``rec_id``, and ``color``)
are copied onto the ``updated`` item.

Drag is enabled only when the field is not readonly, ``editable`` is
true, and the coordinates are valid.

Payload updates
---------------

When the field value changes (for example a computed JSON that
recomputes):

- Changes to ``elements`` (or other marker data) rebuild markers only.
  The camera is **not** reset.
- Changes to camera keys (``default_center``, ``default_zoom``,
  ``default_pitch``), style URLs, or ``allow_zoom`` / ``allow_pan`` /
  ``allow_pitch`` / ``allow_fullscreen`` recreate the map so the new
  config applies.
- Switching to another record always recreates the map for that record’s
  payload.

Computed non-stored JSON
------------------------

Display works on a computed JSON field that is **not** stored. Odoo
defaults ``store=False`` and ``readonly=True`` unless an inverse is set,
so the map still renders when the field is in the view.

``updated`` is persisted only when the field is writable (stored, or
computed with an inverse / ``readonly=False``). Do not set
``store=True`` unless the consuming module needs those writes.

Example of a computed non-stored field (typical consumer; **not** added
by this addon):

.. code:: python

   map_data = fields.Json(compute="_compute_map_data")

   def _compute_map_data(self):
       for rec in self:
           rec.map_data = {
               "elements": [...],
               "default_center": {
                   "lat": rec.partner_latitude,
                   "lon": rec.partner_longitude,
               },
               "default_zoom": 12,
           }

Known issues / Roadmap
======================

- Clustering, drawing, routing, and GeoJSON polygons are not in scope.
- The widget does not add, delete, or geocode markers in the UI.
- Website and portal assets are not loaded; this is a backend form
  widget.

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/geospatial/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/geospatial/issues/new?body=module:%20web_widget_mapbox%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
-------

* Cetmix

Contributors
------------

- `Cetmix OÜ <https://cetmix.com>`__:

  - Ivan Sokolov

Other credits
-------------

This module loads `Mapbox GL
JS <https://github.com/mapbox/mapbox-gl-js>`__ **v3.30.0** from the
Mapbox CDN at runtime
(``https://api.mapbox.com/mapbox-gl-js/v3.30.0/``). The library is
**not** copied into this repository.

Mapbox GL JS v2 and later is licensed under the Mapbox Terms of Service,
not an OSI license. Using this widget requires a Mapbox account and
compliance with those terms: https://www.mapbox.com/legal/tos.

Maintainers
-----------

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
   :alt: Odoo Community Association
   :target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

This module is part of the `OCA/geospatial <https://github.com/OCA/geospatial/tree/18.0/web_widget_mapbox>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
