odoo-addon-web-view-leaflet-map 16.0.2.0.0.3__py3-none-any.whl → 18.0.1.1.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- odoo/addons/web_view_leaflet_map/README.rst +96 -83
- odoo/addons/web_view_leaflet_map/__manifest__.py +7 -6
- odoo/addons/web_view_leaflet_map/hooks.py +4 -4
- odoo/addons/web_view_leaflet_map/i18n/fr.po +7 -5
- odoo/addons/web_view_leaflet_map/i18n/it.po +7 -5
- odoo/addons/web_view_leaflet_map/i18n/web_view_leaflet_map.pot +3 -5
- odoo/addons/web_view_leaflet_map/models/ir_ui_view.py +3 -0
- odoo/addons/web_view_leaflet_map/readme/CONFIGURE.md +1 -0
- odoo/addons/web_view_leaflet_map/readme/CONTRIBUTORS.md +1 -0
- odoo/addons/web_view_leaflet_map/readme/DESCRIPTION.md +23 -0
- odoo/addons/web_view_leaflet_map/readme/DEVELOP.md +53 -0
- odoo/addons/web_view_leaflet_map/readme/ROADMAP.md +13 -0
- odoo/addons/web_view_leaflet_map/static/description/index.html +69 -62
- odoo/addons/web_view_leaflet_map/static/description/res_partner_large.png +0 -0
- odoo/addons/web_view_leaflet_map/static/description/res_partner_precise.png +0 -0
- odoo/addons/web_view_leaflet_map/static/src/views/leaflet_map/leaflet_map_controller.esm.js +24 -0
- odoo/addons/web_view_leaflet_map/static/src/views/leaflet_map/leaflet_map_controller.xml +24 -0
- odoo/addons/web_view_leaflet_map/static/src/views/leaflet_map/leaflet_map_renderer.esm.js +275 -0
- odoo/addons/web_view_leaflet_map/static/src/views/leaflet_map/leaflet_map_renderer.xml +10 -0
- odoo/addons/web_view_leaflet_map/static/src/views/leaflet_map/leaflet_map_view.esm.js +53 -0
- odoo_addon_web_view_leaflet_map-18.0.1.1.2.1.dist-info/METADATA +212 -0
- odoo_addon_web_view_leaflet_map-18.0.1.1.2.1.dist-info/RECORD +30 -0
- {odoo_addon_web_view_leaflet_map-16.0.2.0.0.3.dist-info → odoo_addon_web_view_leaflet_map-18.0.1.1.2.1.dist-info}/WHEEL +1 -1
- odoo_addon_web_view_leaflet_map-18.0.1.1.2.1.dist-info/top_level.txt +1 -0
- odoo/addons/web_view_leaflet_map/readme/CONFIGURE.rst +0 -1
- odoo/addons/web_view_leaflet_map/readme/CONTRIBUTORS.rst +0 -1
- odoo/addons/web_view_leaflet_map/readme/DESCRIPTION.rst +0 -11
- odoo/addons/web_view_leaflet_map/readme/DEVELOP.rst +0 -62
- odoo/addons/web_view_leaflet_map/readme/ROADMAP.rst +0 -8
- odoo/addons/web_view_leaflet_map/static/description/res_partner_map.png +0 -0
- odoo/addons/web_view_leaflet_map/static/src/js/view/map/map_renderer.js +0 -178
- odoo/addons/web_view_leaflet_map/static/src/js/view/map/map_view.js +0 -34
- odoo/addons/web_view_leaflet_map/static/src/js/view/view_registry.js +0 -8
- odoo_addon_web_view_leaflet_map-16.0.2.0.0.3.dist-info/METADATA +0 -199
- odoo_addon_web_view_leaflet_map-16.0.2.0.0.3.dist-info/RECORD +0 -27
- odoo_addon_web_view_leaflet_map-16.0.2.0.0.3.dist-info/top_level.txt +0 -1
- /odoo/addons/web_view_leaflet_map/static/src/{css/web_view_leaflet_map.css → views/leaflet_map/leaflet_map_renderer.css} +0 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
5
5
|
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
|
|
6
|
-
<title>
|
|
6
|
+
<title>README.rst</title>
|
|
7
7
|
<style type="text/css">
|
|
8
8
|
|
|
9
9
|
/*
|
|
@@ -360,34 +360,37 @@ ul.auto-toc {
|
|
|
360
360
|
</style>
|
|
361
361
|
</head>
|
|
362
362
|
<body>
|
|
363
|
-
<div class="document"
|
|
364
|
-
<h1 class="title">Leaflet Map View (OpenStreetMap)</h1>
|
|
363
|
+
<div class="document">
|
|
365
364
|
|
|
365
|
+
|
|
366
|
+
<a class="reference external image-reference" href="https://odoo-community.org/get-involved?utm_source=readme">
|
|
367
|
+
<img alt="Odoo Community Association" src="https://odoo-community.org/readme-banner-image" />
|
|
368
|
+
</a>
|
|
369
|
+
<div class="section" id="leaflet-map-view-openstreetmap">
|
|
370
|
+
<h1>Leaflet Map View (OpenStreetMap)</h1>
|
|
366
371
|
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
367
372
|
!! This file is generated by oca-gen-addon-readme !!
|
|
368
373
|
!! changes will be overwritten. !!
|
|
369
374
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
370
|
-
!! source digest: sha256:
|
|
375
|
+
!! source digest: sha256:74bf9361fbf3e37a4355f272c0cfb21bff1711d71639a7164dfe915bfc7773ab
|
|
371
376
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
|
372
|
-
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="
|
|
373
|
-
<p>This module extends odoo views, to add a new kind of view, named
|
|
374
|
-
that is using the Leaflet javascript library to use
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
</
|
|
382
|
-
<
|
|
383
|
-
<img alt="
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
<p
|
|
387
|
-
<
|
|
388
|
-
|
|
389
|
-
<a class="reference external" href="https://odoo-community.org/page/development-status">More details on development status</a></p>
|
|
390
|
-
</div>
|
|
377
|
+
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/license-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/geospatial/tree/18.0/web_view_leaflet_map"><img alt="OCA/geospatial" src="https://img.shields.io/badge/github-OCA%2Fgeospatial-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/geospatial-18-0/geospatial-18-0-web_view_leaflet_map"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/geospatial&target_branch=18.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
|
|
378
|
+
<p>This module extends odoo views, to add a new kind of view, named
|
|
379
|
+
<tt class="docutils literal">leaflet_map</tt> that is using the Leaflet javascript library to use
|
|
380
|
+
maps. (<a class="reference external" href="https://leafletjs.com/">https://leafletjs.com/</a>) This library is for exemple, used in the
|
|
381
|
+
OpenStreetMap project. (<a class="reference external" href="https://www.openstreetmap.org/">https://www.openstreetmap.org/</a>)</p>
|
|
382
|
+
<p>You can see a simple usage in the module
|
|
383
|
+
<tt class="docutils literal">web_view_leaflet_map_partner</tt> in the same OCA repository that
|
|
384
|
+
displays your contact in a map, if latitude and longitude are defined.
|
|
385
|
+
(To define latitude and longitude, refer to the Odoo module
|
|
386
|
+
<tt class="docutils literal">base_geolocalize</tt>)</p>
|
|
387
|
+
<p>A marker will be displayed for each item that has a localization.</p>
|
|
388
|
+
<p><img alt="image1" src="https://raw.githubusercontent.com/OCA/geospatial/18.0/web_view_leaflet_map/static/description/view_res_partner_map_precise.png" /></p>
|
|
389
|
+
<p>If user zooms out, the markers will overlap, which won’t be very
|
|
390
|
+
visible.</p>
|
|
391
|
+
<p>In that case, nearby markers are grouped together, thanks to
|
|
392
|
+
<tt class="docutils literal">Leaflet.markercluster</tt> plugin.</p>
|
|
393
|
+
<p><img alt="image2" src="https://raw.githubusercontent.com/OCA/geospatial/18.0/web_view_leaflet_map/static/description/view_res_partner_map_large.png" /></p>
|
|
391
394
|
<p><strong>Table of contents</strong></p>
|
|
392
395
|
<div class="contents local topic" id="contents">
|
|
393
396
|
<ul class="simple">
|
|
@@ -404,13 +407,13 @@ Only for development or testing purpose, do not use in production.
|
|
|
404
407
|
</ul>
|
|
405
408
|
</div>
|
|
406
409
|
<div class="section" id="configuration">
|
|
407
|
-
<
|
|
410
|
+
<h2><a class="toc-backref" href="#toc-entry-1">Configuration</a></h2>
|
|
408
411
|
<ul class="simple">
|
|
409
412
|
<li>See configuration of the module <tt class="docutils literal">web_leaflet_lib</tt>.</li>
|
|
410
413
|
</ul>
|
|
411
414
|
</div>
|
|
412
415
|
<div class="section" id="development">
|
|
413
|
-
<
|
|
416
|
+
<h2><a class="toc-backref" href="#toc-entry-2">Development</a></h2>
|
|
414
417
|
<p>Create a new view :</p>
|
|
415
418
|
<pre class="code xml literal-block">
|
|
416
419
|
<span class="nt"><record</span><span class="w"> </span><span class="na">id=</span><span class="s">"view_my_model_map"</span><span class="w"> </span><span class="na">model=</span><span class="s">"ir.ui.view"</span><span class="nt">></span><span class="w">
|
|
@@ -433,22 +436,23 @@ Only for development or testing purpose, do not use in production.
|
|
|
433
436
|
</span><span class="nt"></record></span>
|
|
434
437
|
</pre>
|
|
435
438
|
<ol class="arabic simple">
|
|
436
|
-
<li>FIELD_LATITUDE and FIELD_LONGITUDE are the name of the fields that
|
|
439
|
+
<li>FIELD_LATITUDE and FIELD_LONGITUDE are the name of the fields that
|
|
440
|
+
contains GPS coordinates of the model.</li>
|
|
437
441
|
<li>FIELD_TITLE will be used when the popup is displayed, as a title.</li>
|
|
438
|
-
<li>FIELD_ADDRESS will be used when the popup is displayed to display the
|
|
439
|
-
|
|
440
|
-
of the
|
|
441
|
-
Note: You can set extra settings
|
|
442
|
-
|
|
443
|
-
the
|
|
442
|
+
<li>FIELD_ADDRESS will be used when the popup is displayed to display the
|
|
443
|
+
adress.</li>
|
|
444
|
+
<li>(optional) FIELD_MARKER_ICON_IMAGE, is the name of the image field to
|
|
445
|
+
place as an icon of the marker. Note: You can set extra settings
|
|
446
|
+
<tt class="docutils literal">marker_icon_size_x</tt>, <tt class="docutils literal">marker_icon_size_y</tt>, to define the size of
|
|
447
|
+
the image, and <tt class="docutils literal">marker_popup_anchor_x</tt>, <tt class="docutils literal">marker_popup_anchor_y</tt>
|
|
448
|
+
to define the position of the popup.</li>
|
|
444
449
|
</ol>
|
|
445
450
|
<p>Map options :</p>
|
|
446
451
|
<ul class="simple">
|
|
447
452
|
<li><tt class="docutils literal">default_zoom</tt> : define the default zoom value. (7 if not defined)</li>
|
|
448
453
|
<li><tt class="docutils literal">max_zoom</tt> : define the max zoom value. (19 if not defined)</li>
|
|
449
|
-
<li><tt class="docutils literal">zoom_snap</tt> : define the zoom level in each change. (1 if not
|
|
450
|
-
</
|
|
451
|
-
<ul class="simple">
|
|
454
|
+
<li><tt class="docutils literal">zoom_snap</tt> : define the zoom level in each change. (1 if not
|
|
455
|
+
defined)</li>
|
|
452
456
|
<li>Create or update an action for the model</li>
|
|
453
457
|
</ul>
|
|
454
458
|
<pre class="code xml literal-block">
|
|
@@ -456,56 +460,58 @@ the position of the popup.</li>
|
|
|
456
460
|
</span><span class="nt"><field</span><span class="w"> </span><span class="na">name=</span><span class="s">"view_mode"</span><span class="nt">></span>tree,form,leaflet_map<span class="nt"></field></span><span class="w">
|
|
457
461
|
</span><span class="nt"></record></span>
|
|
458
462
|
</pre>
|
|
459
|
-
<p><strong>Library Update</strong></p>
|
|
460
|
-
<p>For the time being, the module embed the lealflet.js library version 1.8.0 ( released on April 18, 2022.)</p>
|
|
461
|
-
<p>If a new release is out:</p>
|
|
462
|
-
<ul class="simple">
|
|
463
|
-
<li>please download it here <a class="reference external" href="https://leafletjs.com/download.html">https://leafletjs.com/download.html</a></li>
|
|
464
|
-
<li>update the javascript, css and images, present in the folder <tt class="docutils literal">static/lib/leaflet</tt></li>
|
|
465
|
-
<li>test the features</li>
|
|
466
|
-
<li>make a Pull Request</li>
|
|
467
|
-
</ul>
|
|
468
463
|
<p><strong>Default position in the map</strong></p>
|
|
469
|
-
<p>By default, the position of the map is defined by the user, in the
|
|
470
|
-
<tt class="docutils literal">get_default_leaflet_position</tt>. It returns the position of
|
|
471
|
-
you can overload this function
|
|
464
|
+
<p>By default, the position of the map is defined by the user, in the
|
|
465
|
+
function <tt class="docutils literal">get_default_leaflet_position</tt>. It returns the position of
|
|
466
|
+
the current company, if defined. you can overload this function
|
|
467
|
+
globally, or per model.</p>
|
|
472
468
|
</div>
|
|
473
469
|
<div class="section" id="known-issues-roadmap">
|
|
474
|
-
<
|
|
470
|
+
<h2><a class="toc-backref" href="#toc-entry-3">Known issues / Roadmap</a></h2>
|
|
475
471
|
<ul class="simple">
|
|
476
|
-
<li>For the time being, at the start of the map loading, the call of
|
|
477
|
-
is required. We should investigate why and try to
|
|
478
|
-
|
|
479
|
-
<
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
472
|
+
<li>For the time being, at the start of the map loading, the call of
|
|
473
|
+
<tt class="docutils literal">invalidateSize()</tt> is required. We should investigate why and try to
|
|
474
|
+
remove that call. see
|
|
475
|
+
<a class="reference external" href="https://github.com/Leaflet/Leaflet/issues/3002#issuecomment-93836022">https://github.com/Leaflet/Leaflet/issues/3002#issuecomment-93836022</a></li>
|
|
476
|
+
<li>For the time being, the map has “Markers” and allow to display odoo
|
|
477
|
+
items if longitude and latitude are available. We could imagine other
|
|
478
|
+
kind of usages, with Polylines, Polygons, etc… See all the leaflet
|
|
479
|
+
options : <a class="reference external" href="https://leafletjs.com/reference.html">https://leafletjs.com/reference.html</a></li>
|
|
480
|
+
<li>Search bar is not implemented in this view. All records are displayed
|
|
481
|
+
for now. We should:<ul>
|
|
482
|
+
<li>implement records refresh, when adding / removing domain in the
|
|
483
|
+
search bar.</li>
|
|
484
|
+
<li>implement a custom search based on the displayed map. (no need to
|
|
485
|
+
load records that are out of the scope of the current displayed
|
|
486
|
+
map).</li>
|
|
487
|
+
</ul>
|
|
488
|
+
</li>
|
|
483
489
|
</ul>
|
|
484
490
|
</div>
|
|
485
491
|
<div class="section" id="bug-tracker">
|
|
486
|
-
<
|
|
492
|
+
<h2><a class="toc-backref" href="#toc-entry-4">Bug Tracker</a></h2>
|
|
487
493
|
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/geospatial/issues">GitHub Issues</a>.
|
|
488
494
|
In case of trouble, please check there if your issue has already been reported.
|
|
489
495
|
If you spotted it first, help us to smash it by providing a detailed and welcomed
|
|
490
|
-
<a class="reference external" href="https://github.com/OCA/geospatial/issues/new?body=module:%20web_view_leaflet_map%0Aversion:%
|
|
496
|
+
<a class="reference external" href="https://github.com/OCA/geospatial/issues/new?body=module:%20web_view_leaflet_map%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
|
|
491
497
|
<p>Do not contact contributors directly about support or help with technical issues.</p>
|
|
492
498
|
</div>
|
|
493
499
|
<div class="section" id="credits">
|
|
494
|
-
<
|
|
500
|
+
<h2><a class="toc-backref" href="#toc-entry-5">Credits</a></h2>
|
|
495
501
|
<div class="section" id="authors">
|
|
496
|
-
<
|
|
502
|
+
<h3><a class="toc-backref" href="#toc-entry-6">Authors</a></h3>
|
|
497
503
|
<ul class="simple">
|
|
498
504
|
<li>GRAP</li>
|
|
499
505
|
</ul>
|
|
500
506
|
</div>
|
|
501
507
|
<div class="section" id="contributors">
|
|
502
|
-
<
|
|
508
|
+
<h3><a class="toc-backref" href="#toc-entry-7">Contributors</a></h3>
|
|
503
509
|
<ul class="simple">
|
|
504
510
|
<li>Sylvain LE GAL (<a class="reference external" href="https://www.twitter.com/legalsylvain">https://www.twitter.com/legalsylvain</a>)</li>
|
|
505
511
|
</ul>
|
|
506
512
|
</div>
|
|
507
513
|
<div class="section" id="maintainers">
|
|
508
|
-
<
|
|
514
|
+
<h3><a class="toc-backref" href="#toc-entry-8">Maintainers</a></h3>
|
|
509
515
|
<p>This module is maintained by the OCA.</p>
|
|
510
516
|
<a class="reference external image-reference" href="https://odoo-community.org">
|
|
511
517
|
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
|
|
@@ -515,10 +521,11 @@ mission is to support the collaborative development of Odoo features and
|
|
|
515
521
|
promote its widespread use.</p>
|
|
516
522
|
<p>Current <a class="reference external" href="https://odoo-community.org/page/maintainer-role">maintainer</a>:</p>
|
|
517
523
|
<p><a class="reference external image-reference" href="https://github.com/legalsylvain"><img alt="legalsylvain" src="https://github.com/legalsylvain.png?size=40px" /></a></p>
|
|
518
|
-
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/geospatial/tree/
|
|
524
|
+
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/geospatial/tree/18.0/web_view_leaflet_map">OCA/geospatial</a> project on GitHub.</p>
|
|
519
525
|
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
|
|
520
526
|
</div>
|
|
521
527
|
</div>
|
|
522
528
|
</div>
|
|
529
|
+
</div>
|
|
523
530
|
</body>
|
|
524
531
|
</html>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import {Component, useRef} from "@odoo/owl";
|
|
2
|
+
import {Layout} from "@web/search/layout";
|
|
3
|
+
import {MapRenderer} from "./leaflet_map_renderer.esm";
|
|
4
|
+
import {executeButtonCallback} from "@web/views/view_button/view_button_hook";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Controller class for the Map view, setting up the environment configuration.
|
|
8
|
+
*/
|
|
9
|
+
export class MapController extends Component {
|
|
10
|
+
static template = "web_view_leaflet_map.MapView";
|
|
11
|
+
static components = {Layout, MapRenderer};
|
|
12
|
+
|
|
13
|
+
setup() {
|
|
14
|
+
this.rootRef = useRef("root");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async onClickCreate() {
|
|
18
|
+
return executeButtonCallback(this.rootRef.el, () => this.createRecord());
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async createRecord() {
|
|
22
|
+
await this.props.createRecord();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" ?>
|
|
2
|
+
<templates xml:space="preserve">
|
|
3
|
+
|
|
4
|
+
<t t-name="web_view_leaflet_map.MapView" owl="1">
|
|
5
|
+
<div class="h-100 overflow-auto" t-ref="root">
|
|
6
|
+
<Layout display="props.display" className="'h-100 overflow-auto'">
|
|
7
|
+
<t t-set-slot="control-panel-create-button">
|
|
8
|
+
<button
|
|
9
|
+
type="button"
|
|
10
|
+
class="btn btn-primary o_list_button_add"
|
|
11
|
+
data-hotkey="c"
|
|
12
|
+
t-on-click="onClickCreate"
|
|
13
|
+
data-bounce-button=""
|
|
14
|
+
>
|
|
15
|
+
New
|
|
16
|
+
</button>
|
|
17
|
+
</t>
|
|
18
|
+
|
|
19
|
+
<MapRenderer t-props="props" />
|
|
20
|
+
</Layout>
|
|
21
|
+
</div>
|
|
22
|
+
</t>
|
|
23
|
+
|
|
24
|
+
</templates>
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import {session} from "@web/session";
|
|
2
|
+
import {useService} from "@web/core/utils/hooks";
|
|
3
|
+
|
|
4
|
+
/* global L, console, document */
|
|
5
|
+
|
|
6
|
+
const {Component, onWillStart, onMounted, onPatched, useRef} = owl;
|
|
7
|
+
|
|
8
|
+
export class MapRenderer extends Component {
|
|
9
|
+
static template = "web_view_leaflet_map.MapRenderer";
|
|
10
|
+
static components = {};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Initializes the MapRenderer component, setting up services, references, and configuration.
|
|
14
|
+
*/
|
|
15
|
+
// eslint-disable-next-line complexity
|
|
16
|
+
setup() {
|
|
17
|
+
this.orm = useService("orm");
|
|
18
|
+
this.action = useService("action");
|
|
19
|
+
this.mapRef = useRef("mapContainer");
|
|
20
|
+
this.leafletTileUrl = session["leaflet.tile_url"];
|
|
21
|
+
this.leafletCopyright = session["leaflet.copyright"];
|
|
22
|
+
|
|
23
|
+
const archAttrs = this.props.archInfo.arch.attributes;
|
|
24
|
+
|
|
25
|
+
this.resModel = this.props.resModel;
|
|
26
|
+
this.defaultZoom = parseInt(archAttrs.default_zoom, 10) || 7;
|
|
27
|
+
this.maxZoom = parseInt(archAttrs.max_zoom, 10) || 19;
|
|
28
|
+
this.zoomSnap = parseInt(archAttrs.zoom_snap, 10) || 1;
|
|
29
|
+
|
|
30
|
+
this.fieldLatitude = archAttrs.field_latitude?.value;
|
|
31
|
+
this.fieldLongitude = archAttrs.field_longitude?.value;
|
|
32
|
+
this.fieldTitle = archAttrs.field_title?.value;
|
|
33
|
+
this.fieldAddress = archAttrs.field_address?.value;
|
|
34
|
+
this.fieldMarkerIconImage = archAttrs.field_marker_icon_image?.value;
|
|
35
|
+
|
|
36
|
+
this.markerIconSizeX = parseInt(archAttrs.marker_icon_size_x?.value, 10) || 64;
|
|
37
|
+
this.markerIconSizeY = parseInt(archAttrs.marker_icon_size_y?.value, 10) || 64;
|
|
38
|
+
this.markerPopupAnchorX =
|
|
39
|
+
parseInt(archAttrs.marker_popup_anchor_x?.value, 10) || 0;
|
|
40
|
+
this.markerPopupAnchorY =
|
|
41
|
+
parseInt(archAttrs.marker_popup_anchor_y?.value, 10) || -32;
|
|
42
|
+
|
|
43
|
+
this.leafletMap = null;
|
|
44
|
+
this.mainLayer = null;
|
|
45
|
+
|
|
46
|
+
onWillStart(async () => {
|
|
47
|
+
await this.initDefaultPosition();
|
|
48
|
+
await this.loadRecords();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
onMounted(() => {
|
|
52
|
+
this.initMap();
|
|
53
|
+
this.renderMarkers();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
onPatched(() => {
|
|
57
|
+
if (this.leafletMap) {
|
|
58
|
+
this.renderMarkers();
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Loads records from the server based on the provided domain and fields.
|
|
65
|
+
* @returns {Promise<void>}
|
|
66
|
+
*/
|
|
67
|
+
async loadRecords() {
|
|
68
|
+
const fields = this.getFields();
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
// Cargar registros usando searchRead
|
|
72
|
+
const records = await this.orm.searchRead(
|
|
73
|
+
this.resModel,
|
|
74
|
+
this.props.domain || [],
|
|
75
|
+
fields,
|
|
76
|
+
{
|
|
77
|
+
limit: this.props.limit || 80,
|
|
78
|
+
context: this.props.context || {},
|
|
79
|
+
}
|
|
80
|
+
);
|
|
81
|
+
this.records = records;
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.error("Error loading records:", error);
|
|
84
|
+
this.records = [];
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Gathers the required fields for the map view.
|
|
90
|
+
* @returns {any[]}
|
|
91
|
+
*/
|
|
92
|
+
getFields() {
|
|
93
|
+
const fields = new Set();
|
|
94
|
+
|
|
95
|
+
// Required fields
|
|
96
|
+
fields.add("id");
|
|
97
|
+
fields.add("display_name");
|
|
98
|
+
fields.add("date_localization");
|
|
99
|
+
|
|
100
|
+
// Optional fields based on arch attributes
|
|
101
|
+
if (this.fieldLatitude) fields.add(this.fieldLatitude);
|
|
102
|
+
if (this.fieldLongitude) fields.add(this.fieldLongitude);
|
|
103
|
+
if (this.fieldTitle) fields.add(this.fieldTitle);
|
|
104
|
+
if (this.fieldAddress) fields.add(this.fieldAddress);
|
|
105
|
+
if (this.fieldMarkerIconImage) fields.add(this.fieldMarkerIconImage);
|
|
106
|
+
|
|
107
|
+
return Array.from(fields);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Initializes the default position of the map by calling the server method.
|
|
112
|
+
* @returns {Promise<void>}
|
|
113
|
+
*/
|
|
114
|
+
async initDefaultPosition() {
|
|
115
|
+
const result = await this.orm.call(
|
|
116
|
+
"res.users",
|
|
117
|
+
"get_default_leaflet_position",
|
|
118
|
+
[this.props.resModel]
|
|
119
|
+
);
|
|
120
|
+
this.defaultLatLng = L.latLng(result.lat, result.lng);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Initializes the Leaflet map in the container.
|
|
125
|
+
*/
|
|
126
|
+
initMap() {
|
|
127
|
+
const mapDiv = this.mapRef.el;
|
|
128
|
+
if (!mapDiv) {
|
|
129
|
+
console.error("Map container not found");
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
this.leafletMap = L.map(mapDiv, {
|
|
134
|
+
zoomSnap: this.zoomSnap,
|
|
135
|
+
}).setView(this.defaultLatLng, this.defaultZoom);
|
|
136
|
+
|
|
137
|
+
L.tileLayer(this.leafletTileUrl, {
|
|
138
|
+
maxZoom: this.maxZoom,
|
|
139
|
+
attribution: this.leafletCopyright,
|
|
140
|
+
}).addTo(this.leafletMap);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Renders the markers on the map based on the loaded records.
|
|
145
|
+
*/
|
|
146
|
+
renderMarkers() {
|
|
147
|
+
if (!this.leafletMap) {
|
|
148
|
+
console.warn("Map not initialized yet");
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (this.mainLayer) {
|
|
153
|
+
this.leafletMap.removeLayer(this.mainLayer);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
this.mainLayer = L.markerClusterGroup();
|
|
157
|
+
for (const record of this.records) {
|
|
158
|
+
const marker = this.prepareMarker(record);
|
|
159
|
+
if (marker) {
|
|
160
|
+
this.mainLayer.addLayer(marker);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
const bounds = this.mainLayer.getBounds();
|
|
164
|
+
if (bounds.isValid()) {
|
|
165
|
+
// Adapt the map's position based on the map's points
|
|
166
|
+
this.leafletMap.fitBounds(bounds.pad(0.1));
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
this.leafletMap.addLayer(this.mainLayer);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Prepares a Leaflet marker for the given record.
|
|
174
|
+
* @param {Object} record - The record object containing marker data
|
|
175
|
+
* @returns {*}
|
|
176
|
+
*/
|
|
177
|
+
prepareMarker(record) {
|
|
178
|
+
const lat = record[this.fieldLatitude];
|
|
179
|
+
const lng = record[this.fieldLongitude];
|
|
180
|
+
let marker = null;
|
|
181
|
+
if (!lat || !lng) {
|
|
182
|
+
console.debug(`Record ${record.id} has no coordinates`);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const latlng = L.latLng(lat, lng);
|
|
187
|
+
if (latlng.lat !== 0 && latlng.lng !== 0) {
|
|
188
|
+
const markerOptions = this.prepareMarkerOptions(record);
|
|
189
|
+
|
|
190
|
+
marker = L.marker(latlng, markerOptions);
|
|
191
|
+
const popup = L.popup().setContent(this.preparePopUpData(record));
|
|
192
|
+
|
|
193
|
+
marker.bindPopup(popup).on("popupopen", () => {
|
|
194
|
+
const selector = document.querySelector(".o_map_selector");
|
|
195
|
+
if (selector) {
|
|
196
|
+
selector.addEventListener("click", (ev) => {
|
|
197
|
+
ev.preventDefault();
|
|
198
|
+
this.onClickLeafletPopup(record);
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
return marker;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Prepares the Leaflet icon for the marker using the image field.
|
|
209
|
+
* @param {Object} record - The record object containing marker data
|
|
210
|
+
* @returns {*}
|
|
211
|
+
*/
|
|
212
|
+
prepareMarkerIcon(record) {
|
|
213
|
+
const lastUpdate = record.date_localization || new Date().toISOString();
|
|
214
|
+
const unique = lastUpdate.replace(/[^0-9]/g, "");
|
|
215
|
+
const iconUrl = `/web/image?model=${this.resModel}&id=${record.id}&field=${this.fieldMarkerIconImage}&unique=${unique}`;
|
|
216
|
+
|
|
217
|
+
return L.icon({
|
|
218
|
+
iconUrl: iconUrl,
|
|
219
|
+
className: "leaflet_marker_icon",
|
|
220
|
+
iconSize: [this.markerIconSizeX, this.markerIconSizeY],
|
|
221
|
+
popupAnchor: [this.markerPopupAnchorX, this.markerPopupAnchorY],
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Prepares the options for the leaflet marker.
|
|
227
|
+
* @param {Object} record - The record object containing marker data
|
|
228
|
+
* @returns {{riseOnHover: Boolean, alt: (*|string), title: (*|string)}}
|
|
229
|
+
*/
|
|
230
|
+
prepareMarkerOptions(record) {
|
|
231
|
+
const title = record[this.fieldTitle] || "";
|
|
232
|
+
const result = {
|
|
233
|
+
title: title,
|
|
234
|
+
alt: title,
|
|
235
|
+
riseOnHover: true,
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
if (this.fieldMarkerIconImage) {
|
|
239
|
+
result.icon = this.prepareMarkerIcon(record);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return result;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Prepares the HTML content for the leaflet popup.
|
|
247
|
+
* @param {Object} record - The record object containing marker data
|
|
248
|
+
* @returns {String}
|
|
249
|
+
*/
|
|
250
|
+
preparePopUpData(record) {
|
|
251
|
+
const title = record[this.fieldTitle] || "";
|
|
252
|
+
const address = record[this.fieldAddress] || "";
|
|
253
|
+
|
|
254
|
+
return `
|
|
255
|
+
<div class='o_map_selector' data-res-id='${record.resId}'>
|
|
256
|
+
<b>${title}</b><br/>
|
|
257
|
+
${address ? ` - ${address}` : ""}
|
|
258
|
+
</div>
|
|
259
|
+
`;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Handles click on the leaflet popup to open the record form view.
|
|
264
|
+
* @param {Object} record - The record object containing marker data
|
|
265
|
+
*/
|
|
266
|
+
onClickLeafletPopup(record) {
|
|
267
|
+
this.action.doAction({
|
|
268
|
+
type: "ir.actions.act_window",
|
|
269
|
+
res_model: this.resModel,
|
|
270
|
+
res_id: record.id,
|
|
271
|
+
views: [[false, "form"]],
|
|
272
|
+
target: "current",
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" ?>
|
|
2
|
+
<templates xml:space="preserve">
|
|
3
|
+
|
|
4
|
+
<t t-name="web_view_leaflet_map.MapRenderer" owl="1">
|
|
5
|
+
<div class="o_leaflet_main_container h-100" t-ref="root">
|
|
6
|
+
<div t-ref="mapContainer" class="o_leaflet_map_container h-100" />
|
|
7
|
+
</div>
|
|
8
|
+
</t>
|
|
9
|
+
|
|
10
|
+
</templates>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import {MapController} from "./leaflet_map_controller.esm";
|
|
2
|
+
import {MapRenderer} from "./leaflet_map_renderer.esm";
|
|
3
|
+
import {registry} from "@web/core/registry";
|
|
4
|
+
|
|
5
|
+
/* global DOMParser */
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Helper function that normalize the architecture input to ensure it is an HTMLElement.
|
|
9
|
+
* @param arch
|
|
10
|
+
* @returns {HTMLElement|*}
|
|
11
|
+
*/
|
|
12
|
+
function normalizeArch(arch) {
|
|
13
|
+
if (arch && typeof arch !== "string") return arch;
|
|
14
|
+
const xml = String(arch || "");
|
|
15
|
+
const doc = new DOMParser().parseFromString(xml, "text/xml");
|
|
16
|
+
return doc.documentElement;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Definition of the map view for Odoo, including its properties and components.
|
|
21
|
+
* @type {{
|
|
22
|
+
* type: string,
|
|
23
|
+
* display_name: string,
|
|
24
|
+
* icon: string,
|
|
25
|
+
* multiRecord: boolean,
|
|
26
|
+
* Controller: MapController,
|
|
27
|
+
* Renderer: MapRenderer,
|
|
28
|
+
* searchMenuTypes: string[],
|
|
29
|
+
* props: (function(*, *): *&{archInfo: {arch: *}, Renderer: MapRenderer})
|
|
30
|
+
* }}
|
|
31
|
+
*/
|
|
32
|
+
export const leafletMapView = {
|
|
33
|
+
type: "leaflet_map",
|
|
34
|
+
display_name: "Map",
|
|
35
|
+
icon: "fa fa-map-o",
|
|
36
|
+
multiRecord: true,
|
|
37
|
+
Controller: MapController,
|
|
38
|
+
Renderer: MapRenderer,
|
|
39
|
+
searchMenuTypes: ["filter", "favorite"],
|
|
40
|
+
|
|
41
|
+
props: (genericProps) => {
|
|
42
|
+
const archEl = normalizeArch(genericProps.arch);
|
|
43
|
+
return {
|
|
44
|
+
...genericProps,
|
|
45
|
+
Renderer: MapRenderer,
|
|
46
|
+
archInfo: {
|
|
47
|
+
arch: archEl,
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
registry.category("views").add("leaflet_map", leafletMapView);
|