BERATools 0.2.0__py3-none-any.whl → 0.2.2__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.
- beratools/__init__.py +1 -7
- beratools/core/algo_centerline.py +491 -351
- beratools/core/algo_common.py +497 -0
- beratools/core/algo_cost.py +192 -0
- beratools/core/{dijkstra_algorithm.py → algo_dijkstra.py} +503 -460
- beratools/core/algo_footprint_rel.py +577 -0
- beratools/core/algo_line_grouping.py +944 -0
- beratools/core/algo_merge_lines.py +214 -0
- beratools/core/algo_split_with_lines.py +304 -0
- beratools/core/algo_tiler.py +428 -0
- beratools/core/algo_vertex_optimization.py +469 -0
- beratools/core/constants.py +52 -86
- beratools/core/logger.py +76 -85
- beratools/core/tool_base.py +196 -133
- beratools/gui/__init__.py +11 -15
- beratools/gui/{beratools.json → assets/beratools.json} +2185 -2300
- beratools/gui/batch_processing_dlg.py +513 -463
- beratools/gui/bt_data.py +481 -487
- beratools/gui/bt_gui_main.py +710 -691
- beratools/gui/main.py +26 -0
- beratools/gui/map_window.py +162 -146
- beratools/gui/tool_widgets.py +725 -493
- beratools/tools/Beratools_r_script.r +1120 -1120
- beratools/tools/Ht_metrics.py +116 -116
- beratools/tools/__init__.py +7 -7
- beratools/tools/batch_processing.py +136 -132
- beratools/tools/canopy_threshold_relative.py +672 -670
- beratools/tools/canopycostraster.py +222 -222
- beratools/tools/centerline.py +136 -176
- beratools/tools/common.py +857 -885
- beratools/tools/fl_regen_csf.py +428 -428
- beratools/tools/forest_line_attributes.py +408 -408
- beratools/tools/line_footprint_absolute.py +213 -363
- beratools/tools/line_footprint_fixed.py +436 -282
- beratools/tools/line_footprint_functions.py +733 -720
- beratools/tools/line_footprint_relative.py +73 -64
- beratools/tools/line_grouping.py +45 -0
- beratools/tools/ln_relative_metrics.py +615 -615
- beratools/tools/r_cal_lpi_elai.r +24 -24
- beratools/tools/r_generate_pd_focalraster.r +100 -100
- beratools/tools/r_interface.py +79 -79
- beratools/tools/r_point_density.r +8 -8
- beratools/tools/rpy_chm2trees.py +86 -86
- beratools/tools/rpy_dsm_chm_by.py +81 -81
- beratools/tools/rpy_dtm_by.py +63 -63
- beratools/tools/rpy_find_cellsize.py +43 -43
- beratools/tools/rpy_gnd_csf.py +74 -74
- beratools/tools/rpy_hummock_hollow.py +85 -85
- beratools/tools/rpy_hummock_hollow_raster.py +71 -71
- beratools/tools/rpy_las_info.py +51 -51
- beratools/tools/rpy_laz2las.py +40 -40
- beratools/tools/rpy_lpi_elai_lascat.py +466 -466
- beratools/tools/rpy_normalized_lidar_by.py +56 -56
- beratools/tools/rpy_percent_above_dbh.py +80 -80
- beratools/tools/rpy_points2trees.py +88 -88
- beratools/tools/rpy_vegcoverage.py +94 -94
- beratools/tools/tiler.py +48 -206
- beratools/tools/tool_template.py +69 -54
- beratools/tools/vertex_optimization.py +61 -620
- beratools/tools/zonal_threshold.py +144 -144
- beratools-0.2.2.dist-info/METADATA +108 -0
- beratools-0.2.2.dist-info/RECORD +74 -0
- {beratools-0.2.0.dist-info → beratools-0.2.2.dist-info}/WHEEL +1 -1
- {beratools-0.2.0.dist-info → beratools-0.2.2.dist-info}/licenses/LICENSE +22 -22
- beratools/gui/cli.py +0 -18
- beratools/gui/gui.json +0 -8
- beratools/gui_tk/ASCII Banners.txt +0 -248
- beratools/gui_tk/__init__.py +0 -20
- beratools/gui_tk/beratools_main.py +0 -515
- beratools/gui_tk/bt_widgets.py +0 -442
- beratools/gui_tk/cli.py +0 -18
- beratools/gui_tk/img/BERALogo.png +0 -0
- beratools/gui_tk/img/closed.gif +0 -0
- beratools/gui_tk/img/closed.png +0 -0
- beratools/gui_tk/img/open.gif +0 -0
- beratools/gui_tk/img/open.png +0 -0
- beratools/gui_tk/img/tool.gif +0 -0
- beratools/gui_tk/img/tool.png +0 -0
- beratools/gui_tk/main.py +0 -14
- beratools/gui_tk/map_window.py +0 -144
- beratools/gui_tk/runner.py +0 -1481
- beratools/gui_tk/tooltip.py +0 -55
- beratools/third_party/pyqtlet2/__init__.py +0 -9
- beratools/third_party/pyqtlet2/leaflet/__init__.py +0 -26
- beratools/third_party/pyqtlet2/leaflet/control/__init__.py +0 -6
- beratools/third_party/pyqtlet2/leaflet/control/control.py +0 -59
- beratools/third_party/pyqtlet2/leaflet/control/draw.py +0 -52
- beratools/third_party/pyqtlet2/leaflet/control/layers.py +0 -20
- beratools/third_party/pyqtlet2/leaflet/core/Parser.py +0 -24
- beratools/third_party/pyqtlet2/leaflet/core/__init__.py +0 -2
- beratools/third_party/pyqtlet2/leaflet/core/evented.py +0 -180
- beratools/third_party/pyqtlet2/leaflet/layer/__init__.py +0 -5
- beratools/third_party/pyqtlet2/leaflet/layer/featuregroup.py +0 -34
- beratools/third_party/pyqtlet2/leaflet/layer/icon/__init__.py +0 -1
- beratools/third_party/pyqtlet2/leaflet/layer/icon/icon.py +0 -30
- beratools/third_party/pyqtlet2/leaflet/layer/imageoverlay.py +0 -18
- beratools/third_party/pyqtlet2/leaflet/layer/layer.py +0 -105
- beratools/third_party/pyqtlet2/leaflet/layer/layergroup.py +0 -45
- beratools/third_party/pyqtlet2/leaflet/layer/marker/__init__.py +0 -1
- beratools/third_party/pyqtlet2/leaflet/layer/marker/marker.py +0 -91
- beratools/third_party/pyqtlet2/leaflet/layer/tile/__init__.py +0 -2
- beratools/third_party/pyqtlet2/leaflet/layer/tile/gridlayer.py +0 -4
- beratools/third_party/pyqtlet2/leaflet/layer/tile/tilelayer.py +0 -16
- beratools/third_party/pyqtlet2/leaflet/layer/vector/__init__.py +0 -5
- beratools/third_party/pyqtlet2/leaflet/layer/vector/circle.py +0 -15
- beratools/third_party/pyqtlet2/leaflet/layer/vector/circlemarker.py +0 -18
- beratools/third_party/pyqtlet2/leaflet/layer/vector/path.py +0 -5
- beratools/third_party/pyqtlet2/leaflet/layer/vector/polygon.py +0 -14
- beratools/third_party/pyqtlet2/leaflet/layer/vector/polyline.py +0 -18
- beratools/third_party/pyqtlet2/leaflet/layer/vector/rectangle.py +0 -14
- beratools/third_party/pyqtlet2/leaflet/map/__init__.py +0 -1
- beratools/third_party/pyqtlet2/leaflet/map/map.py +0 -220
- beratools/third_party/pyqtlet2/mapwidget.py +0 -45
- beratools/third_party/pyqtlet2/web/custom.js +0 -43
- beratools/third_party/pyqtlet2/web/map.html +0 -23
- beratools/third_party/pyqtlet2/web/modules/leaflet_193/images/layers-2x.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_193/images/layers.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_193/images/marker-icon-2x.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_193/images/marker-icon.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_193/images/marker-shadow.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_193/leaflet.css +0 -656
- beratools/third_party/pyqtlet2/web/modules/leaflet_193/leaflet.js +0 -6
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/.codeclimate.yml +0 -14
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/.editorconfig +0 -4
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/.gitattributes +0 -22
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/.travis.yml +0 -43
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/LICENSE +0 -20
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/layers-2x.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/layers.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/marker-icon-2x.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/marker-icon.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/marker-shadow.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/spritesheet-2x.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/spritesheet.png +0 -0
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/images/spritesheet.svg +0 -156
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/leaflet.draw.css +0 -10
- beratools/third_party/pyqtlet2/web/modules/leaflet_draw_414/leaflet.draw.js +0 -10
- beratools/third_party/pyqtlet2/web/modules/leaflet_rotatedMarker_020/LICENSE +0 -22
- beratools/third_party/pyqtlet2/web/modules/leaflet_rotatedMarker_020/leaflet.rotatedMarker.js +0 -57
- beratools/tools/forest_line_ecosite.py +0 -216
- beratools/tools/lapis_all.py +0 -103
- beratools/tools/least_cost_path_from_chm.py +0 -152
- beratools-0.2.0.dist-info/METADATA +0 -63
- beratools-0.2.0.dist-info/RECORD +0 -142
- /beratools/gui/{img → assets}/BERALogo.png +0 -0
- /beratools/gui/{img → assets}/closed.gif +0 -0
- /beratools/gui/{img → assets}/closed.png +0 -0
- /beratools/{gui_tk → gui/assets}/gui.json +0 -0
- /beratools/gui/{img → assets}/open.gif +0 -0
- /beratools/gui/{img → assets}/open.png +0 -0
- /beratools/gui/{img → assets}/tool.gif +0 -0
- /beratools/gui/{img → assets}/tool.png +0 -0
- {beratools-0.2.0.dist-info → beratools-0.2.2.dist-info}/entry_points.txt +0 -0
beratools/gui_tk/tooltip.py
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
# ---------------------------------------------------------------------------
|
|
2
|
-
#
|
|
3
|
-
# Tooltip.py
|
|
4
|
-
# Script Author: SquareRoot17, retrieved at 01/01/2020 from
|
|
5
|
-
# https://stackoverflow.com/questions/20399243/display-message-when-hovering-over-something-with-mouse-cursor-in-python
|
|
6
|
-
# Modified by Gustavo Lopes Queiroz
|
|
7
|
-
# Date: 2020-Jan-22
|
|
8
|
-
#
|
|
9
|
-
# Purpose: Allows for GUI tooltips when user hovers the cursor over elements
|
|
10
|
-
#
|
|
11
|
-
# ---------------------------------------------------------------------------
|
|
12
|
-
try:
|
|
13
|
-
from tkinter import *
|
|
14
|
-
except ImportError:
|
|
15
|
-
from Tkinter import *
|
|
16
|
-
|
|
17
|
-
class ToolTip(object):
|
|
18
|
-
|
|
19
|
-
def __init__(self, widget, wraplength = 0):
|
|
20
|
-
self.widget = widget
|
|
21
|
-
self.tipwindow = None
|
|
22
|
-
self.id = None
|
|
23
|
-
self.x = self.y = 0
|
|
24
|
-
self.wraplength = wraplength
|
|
25
|
-
|
|
26
|
-
def showtip(self, text):
|
|
27
|
-
"Display text in tooltip window"
|
|
28
|
-
self.text = text
|
|
29
|
-
if self.tipwindow or not self.text:
|
|
30
|
-
return
|
|
31
|
-
x, y, cx, cy = self.widget.bbox("insert")
|
|
32
|
-
x = x + self.widget.winfo_rootx() + 57
|
|
33
|
-
y = y + cy + self.widget.winfo_rooty() +27
|
|
34
|
-
self.tipwindow = tw = Toplevel(self.widget)
|
|
35
|
-
tw.wm_overrideredirect(1)
|
|
36
|
-
tw.wm_geometry("+%d+%d" % (x, y))
|
|
37
|
-
label = Label(tw, text=self.text, justify=LEFT,
|
|
38
|
-
background="#ffffe0", relief=SOLID, borderwidth=1, wraplength = self.wraplength,
|
|
39
|
-
font=("tahoma", "8", "normal"))
|
|
40
|
-
label.pack(ipadx=1)
|
|
41
|
-
|
|
42
|
-
def hidetip(self):
|
|
43
|
-
tw = self.tipwindow
|
|
44
|
-
self.tipwindow = None
|
|
45
|
-
if tw:
|
|
46
|
-
tw.destroy()
|
|
47
|
-
|
|
48
|
-
def CreateToolTip(widget, text, wraplength = 500):
|
|
49
|
-
toolTip = ToolTip(widget, wraplength)
|
|
50
|
-
def enter(event):
|
|
51
|
-
toolTip.showtip(text)
|
|
52
|
-
def leave(event):
|
|
53
|
-
toolTip.hidetip()
|
|
54
|
-
widget.bind('<Enter>', enter)
|
|
55
|
-
widget.bind('<Leave>', leave)
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
from .map import Map
|
|
2
|
-
from .layer import LayerGroup, FeatureGroup, imageOverlay
|
|
3
|
-
from .layer.tile import TileLayer
|
|
4
|
-
from .layer.marker import Marker
|
|
5
|
-
from .layer.icon import Icon
|
|
6
|
-
from .layer.vector import Circle, CircleMarker, Polygon, Polyline, Rectangle
|
|
7
|
-
from .control import Control
|
|
8
|
-
|
|
9
|
-
class L:
|
|
10
|
-
"""
|
|
11
|
-
Leaflet namespace that holds reference to all the leaflet objects
|
|
12
|
-
"""
|
|
13
|
-
map = Map
|
|
14
|
-
tileLayer = TileLayer
|
|
15
|
-
imageOverlay = imageOverlay
|
|
16
|
-
marker = Marker
|
|
17
|
-
icon = Icon
|
|
18
|
-
circleMarker = CircleMarker
|
|
19
|
-
polyline = Polyline
|
|
20
|
-
polygon = Polygon
|
|
21
|
-
rectangle = Rectangle
|
|
22
|
-
circle = Circle
|
|
23
|
-
layerGroup = LayerGroup
|
|
24
|
-
featureGroup = FeatureGroup
|
|
25
|
-
control = Control
|
|
26
|
-
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import logging
|
|
3
|
-
import os
|
|
4
|
-
import time
|
|
5
|
-
|
|
6
|
-
from qtpy.QtCore import Slot, Signal
|
|
7
|
-
from ..core import Evented
|
|
8
|
-
|
|
9
|
-
class Control(Evented):
|
|
10
|
-
|
|
11
|
-
# controlId is a static variable shared between all controls
|
|
12
|
-
# It is used to give unique names to controls
|
|
13
|
-
controlId = 0
|
|
14
|
-
# addedToMap and removedFromMap are signals for controls to
|
|
15
|
-
# know when they're added and removed from maps
|
|
16
|
-
addedToMap = Signal()
|
|
17
|
-
removedFromMap = Signal()
|
|
18
|
-
|
|
19
|
-
@property
|
|
20
|
-
def map(self):
|
|
21
|
-
return self._map
|
|
22
|
-
|
|
23
|
-
@map.setter
|
|
24
|
-
def map(self, map_):
|
|
25
|
-
self._map = map_
|
|
26
|
-
if map_ is None:
|
|
27
|
-
self.removedFromMap.emit()
|
|
28
|
-
else:
|
|
29
|
-
self.addedToMap.emit()
|
|
30
|
-
|
|
31
|
-
@property
|
|
32
|
-
def jsName(self):
|
|
33
|
-
return self._controlName
|
|
34
|
-
|
|
35
|
-
@property
|
|
36
|
-
def controlName(self):
|
|
37
|
-
return self._controlName
|
|
38
|
-
|
|
39
|
-
@controlName.setter
|
|
40
|
-
def controlName(self, name):
|
|
41
|
-
self._controlName = name
|
|
42
|
-
|
|
43
|
-
def __init__(self):
|
|
44
|
-
super().__init__()
|
|
45
|
-
self._map = None
|
|
46
|
-
self._controlName = self._getNewControlName()
|
|
47
|
-
|
|
48
|
-
def addTo(self, map_):
|
|
49
|
-
map_.addControl(self)
|
|
50
|
-
return self
|
|
51
|
-
|
|
52
|
-
def removeFrom(self, map_):
|
|
53
|
-
map_.removeControl(self)
|
|
54
|
-
|
|
55
|
-
def _getNewControlName(self):
|
|
56
|
-
controlName = 'c{}'.format(self.controlId)
|
|
57
|
-
Control.controlId += 1
|
|
58
|
-
return controlName
|
|
59
|
-
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
from .control import Control
|
|
2
|
-
# NOTE: Importing FeatureGroup here may not be the best idea
|
|
3
|
-
from ..layer.featuregroup import FeatureGroup
|
|
4
|
-
|
|
5
|
-
DEFAULT_POSITION = 'topleft'
|
|
6
|
-
DEFAULT_CIRCLE = False
|
|
7
|
-
DEFAULT_RECTANGLE = False
|
|
8
|
-
|
|
9
|
-
class Draw(Control):
|
|
10
|
-
|
|
11
|
-
def __init__(self, options={}, handleFeatureGroup=True):
|
|
12
|
-
super().__init__()
|
|
13
|
-
self.options = options
|
|
14
|
-
self.handleFeatureGroup = handleFeatureGroup
|
|
15
|
-
self.featureGroup = None
|
|
16
|
-
self._handleOptions()
|
|
17
|
-
if self._map:
|
|
18
|
-
self._initJs()
|
|
19
|
-
if handleFeatureGroup:
|
|
20
|
-
self.addedToMap.connect(self.addDrawnToFeatureGroup)
|
|
21
|
-
|
|
22
|
-
def _initJs(self):
|
|
23
|
-
jsObject = 'new L.Control.Draw('
|
|
24
|
-
if self.options:
|
|
25
|
-
jsObject += '{options}'.format(options=self._stringifyForJs(self.options))
|
|
26
|
-
jsObject += ')'
|
|
27
|
-
self._createJsObject(jsObject, self._map.mapWidgetIndex)
|
|
28
|
-
|
|
29
|
-
def _handleOptions(self):
|
|
30
|
-
# If there are no options, then we want to set the default options
|
|
31
|
-
self.options['position'] = self.options.get('position', DEFAULT_POSITION)
|
|
32
|
-
draw = self.options.get('draw', {})
|
|
33
|
-
if draw is not False:
|
|
34
|
-
# We want to make sure the user wants draw functionality
|
|
35
|
-
draw['circle'] = draw.get('circle', DEFAULT_CIRCLE)
|
|
36
|
-
draw['rectangle'] = draw.get('rectangle', DEFAULT_RECTANGLE)
|
|
37
|
-
self.options['draw'] = draw
|
|
38
|
-
edit = self.options.get('edit', {})
|
|
39
|
-
if edit is not False:
|
|
40
|
-
# We want to make sure the user wants edit functionality
|
|
41
|
-
featureGroup = edit.get('featureGroup', None)
|
|
42
|
-
if featureGroup is None and self.handleFeatureGroup:
|
|
43
|
-
# If a feature group has not been set, create one and add it
|
|
44
|
-
featureGroup = FeatureGroup()
|
|
45
|
-
edit['featureGroup'] = featureGroup
|
|
46
|
-
self.featureGroup = featureGroup
|
|
47
|
-
self.options['edit'] = edit
|
|
48
|
-
|
|
49
|
-
def addDrawnToFeatureGroup(self):
|
|
50
|
-
self.map.addLayer(self.featureGroup)
|
|
51
|
-
self.map.drawCreated.connect(self.featureGroup.createAndAddDrawnLayer)
|
|
52
|
-
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
from .control import Control
|
|
2
|
-
|
|
3
|
-
class Layers(Control):
|
|
4
|
-
|
|
5
|
-
def __init__(self, layers=[], overlays={}, options=None):
|
|
6
|
-
super().__init__()
|
|
7
|
-
self.layers = layers
|
|
8
|
-
self.overlays = overlays
|
|
9
|
-
self.options = options
|
|
10
|
-
self._initJs()
|
|
11
|
-
|
|
12
|
-
def _initJs(self):
|
|
13
|
-
jsObject = 'L.control.layers({layers}'.format(layers=self._stringifyForJs(self.layers))
|
|
14
|
-
if self.overlays is not None:
|
|
15
|
-
jsObject += ', {overlays}'.format(overlays=self._stringifyForJs(self.overlays))
|
|
16
|
-
if self.options is not None:
|
|
17
|
-
jsObject += ', {options}'.format(options=self._stringifyForJs(self.options))
|
|
18
|
-
jsObject += ')'
|
|
19
|
-
self._createJsObject(jsObject)
|
|
20
|
-
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
class Parser:
|
|
2
|
-
@staticmethod
|
|
3
|
-
def dict_for_js(object_to_parse: dict) -> dict:
|
|
4
|
-
return_dict = {}
|
|
5
|
-
for key, value in object_to_parse.items():
|
|
6
|
-
if isinstance(value, bool):
|
|
7
|
-
return_dict[key] = str(value).lower()
|
|
8
|
-
continue
|
|
9
|
-
|
|
10
|
-
return_dict[key] = value
|
|
11
|
-
return return_dict
|
|
12
|
-
|
|
13
|
-
@staticmethod
|
|
14
|
-
def js_for_dict(object_to_parse: dict) -> dict:
|
|
15
|
-
return_dict = {}
|
|
16
|
-
for key, value in object_to_parse.items():
|
|
17
|
-
if isinstance(value, str):
|
|
18
|
-
value: str
|
|
19
|
-
if value.lower() in ["true", "false"]:
|
|
20
|
-
return_dict[key] = True if value.lower() == "true" else False
|
|
21
|
-
continue
|
|
22
|
-
|
|
23
|
-
return_dict[key] = value
|
|
24
|
-
return return_dict
|
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
|
|
3
|
-
from ... import mapwidget
|
|
4
|
-
|
|
5
|
-
from qtpy.QtCore import QObject, QJsonValue
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class Evented(QObject):
|
|
9
|
-
'''
|
|
10
|
-
Base class for all pyqtlet2 objects.
|
|
11
|
-
Handles initiation, as well as all python<->js communication
|
|
12
|
-
'''
|
|
13
|
-
mapWidgets = []
|
|
14
|
-
|
|
15
|
-
def __init__(self, mapWidget=None, mapWidgetIndex=None):
|
|
16
|
-
'''
|
|
17
|
-
Base class for all pyqtlet2 objects
|
|
18
|
-
Handles initiation, as well as python-Js communication
|
|
19
|
-
The first pyqtlet2 object to be initiated should be pyqtlet2.L.map
|
|
20
|
-
This will allow all the pyqtlet2 objects to have access to the
|
|
21
|
-
widget and thus the ability to implement leaflet via python.
|
|
22
|
-
|
|
23
|
-
:param pyqtlet2.MapWidget mapWidget: The mapwidget object
|
|
24
|
-
Should only be sent once, when the first object is being
|
|
25
|
-
initialised.
|
|
26
|
-
'''
|
|
27
|
-
super().__init__()
|
|
28
|
-
self._logger = logging.getLogger(__name__)
|
|
29
|
-
self.response = None
|
|
30
|
-
|
|
31
|
-
if isinstance(mapWidgetIndex, type(None)):
|
|
32
|
-
return
|
|
33
|
-
|
|
34
|
-
if mapWidget is None:
|
|
35
|
-
raise RuntimeError('L.map must be initialised before other pyqtlet2 objects')
|
|
36
|
-
if not issubclass(type(mapWidget), mapwidget.MapWidget):
|
|
37
|
-
raise TypeError(('Expected mapWidget of type pyqtlet2.MapWidget, '
|
|
38
|
-
'received {type_}'.format(type_=type(mapWidget))))
|
|
39
|
-
self.mapWidgets.append(mapWidget)
|
|
40
|
-
js = ('var channelObjects = null;'
|
|
41
|
-
'new QWebChannel(qt.webChannelTransport, function(channel) {'
|
|
42
|
-
' channelObjects = channel.objects;'
|
|
43
|
-
'});')
|
|
44
|
-
self.runJavaScript(js, mapWidgetIndex)
|
|
45
|
-
if mapWidget := self.getMapWidgetAtIndex(mapWidgetIndex):
|
|
46
|
-
mapWidget.page.titleChanged.connect(lambda: print('title changed'))
|
|
47
|
-
|
|
48
|
-
def getMapWidgetAtIndex(self, mapWidgetIndex):
|
|
49
|
-
if len(self.mapWidgets) > mapWidgetIndex:
|
|
50
|
-
return self.mapWidgets[mapWidgetIndex]
|
|
51
|
-
self._logger.error("No")
|
|
52
|
-
return None
|
|
53
|
-
|
|
54
|
-
def getJsResponse(self, js, mapWidgetIndex, callback):
|
|
55
|
-
'''
|
|
56
|
-
Runs javascript code in the mapWidget and triggers callback.
|
|
57
|
-
|
|
58
|
-
Can be used for custom use cases where information is required
|
|
59
|
-
from the mapwidget, and the existing code does not cover the
|
|
60
|
-
requirement
|
|
61
|
-
|
|
62
|
-
:param str js: The javascript code
|
|
63
|
-
:param function callback: The function that will consume the
|
|
64
|
-
javascript response
|
|
65
|
-
|
|
66
|
-
.. note::
|
|
67
|
-
Qt runs runJavaScript function asynchronously. So if we want
|
|
68
|
-
to get a response from leaflet, we need to force it to be sync
|
|
69
|
-
In all that I have tried, I was unable to get the response from
|
|
70
|
-
the same function, so I am converting it to a method with callback
|
|
71
|
-
'''
|
|
72
|
-
self._logger.debug('Running JS with callback: {js}=>{callback}'.format(
|
|
73
|
-
js=js, callback=callback.__name__))
|
|
74
|
-
if mapWidget := self.getMapWidgetAtIndex(mapWidgetIndex):
|
|
75
|
-
mapWidget.page.runJavaScript(js, callback)
|
|
76
|
-
else:
|
|
77
|
-
self._logger.error(f"Can't find mapWidget at index: {mapWidgetIndex}")
|
|
78
|
-
|
|
79
|
-
def runJavaScript(self, js, mapWidgetIndex: int):
|
|
80
|
-
'''
|
|
81
|
-
Runs javascript code in the mapWidget.
|
|
82
|
-
|
|
83
|
-
Can be used for custom use cases where the existing code,
|
|
84
|
-
methods etc. do not cover the requirements.
|
|
85
|
-
|
|
86
|
-
:param str js: The javascript code
|
|
87
|
-
'''
|
|
88
|
-
self._logger.debug('Running JS: {js}'.format(js=js))
|
|
89
|
-
if mapWidget := self.getMapWidgetAtIndex(mapWidgetIndex):
|
|
90
|
-
mapWidget.page.runJavaScript(js)
|
|
91
|
-
else:
|
|
92
|
-
self._logger.error(f"Can't find mapWidget at index: {mapWidgetIndex}")
|
|
93
|
-
|
|
94
|
-
def _createJsObject(self, leafletJsObject, mapWidgetIndex):
|
|
95
|
-
'''
|
|
96
|
-
Function to create variables/objects in leaflet in the
|
|
97
|
-
javascript "engine", and registers the object so that it can
|
|
98
|
-
be called in the webchannel.
|
|
99
|
-
|
|
100
|
-
:param str leafletJsObject: javascript code that creates the
|
|
101
|
-
leaflet object
|
|
102
|
-
'''
|
|
103
|
-
# Creates the js object on the mapWidget page
|
|
104
|
-
js = 'var {name} = {jsObject}'.format(name=self.jsName,
|
|
105
|
-
jsObject=leafletJsObject)
|
|
106
|
-
self.runJavaScript(js, mapWidgetIndex)
|
|
107
|
-
# register the object in the channel
|
|
108
|
-
if mapWidget := self.getMapWidgetAtIndex(mapWidgetIndex):
|
|
109
|
-
mapWidget.channel.registerObject(
|
|
110
|
-
'{name}Object'.format(name=self.jsName), self)
|
|
111
|
-
else:
|
|
112
|
-
self._logger.error(f"Can't find mapWidget at index: {mapWidgetIndex}")
|
|
113
|
-
|
|
114
|
-
def _connectEventToSignal(self, event, signalEmitter, mapWidgetIndex):
|
|
115
|
-
# We need to delete some keys as they are causing circular structures
|
|
116
|
-
js = '{name}.on("{event}", function(e) {{\
|
|
117
|
-
delete e.target;\
|
|
118
|
-
delete e.sourceTarget;\
|
|
119
|
-
e = copyWithoutCircularReferences([e], e);\
|
|
120
|
-
channelObjects.{name}Object.{signalEmitter}(e)}})'.format(
|
|
121
|
-
name=self.jsName, event=event, signalEmitter=signalEmitter)
|
|
122
|
-
self.runJavaScript(js, mapWidgetIndex)
|
|
123
|
-
|
|
124
|
-
def _stringifyForJs(self, object_):
|
|
125
|
-
# When passing options to JS, sometimes we need to pass in objects
|
|
126
|
-
# this method and _handleObject take care of that
|
|
127
|
-
# Some arguments are strings and some are objects. We also make sure
|
|
128
|
-
# that the objects are not sent as strings. Similarly, we also convert
|
|
129
|
-
# python bool to js bool etc.
|
|
130
|
-
jsString = str(self._handleObject(object_))
|
|
131
|
-
jsString = jsString.replace('\'__pyqtletObjectStart__', '')
|
|
132
|
-
jsString = jsString.replace('\"__pyqtletObjectStart__', '')
|
|
133
|
-
jsString = jsString.replace('__pyqtletObjectEnd__\'', '')
|
|
134
|
-
jsString = jsString.replace('__pyqtletObjectEnd__\"', '')
|
|
135
|
-
return jsString
|
|
136
|
-
|
|
137
|
-
def _handleObject(self, object_):
|
|
138
|
-
if type(object_) is list:
|
|
139
|
-
return [self._handleObject(item) for item in object_]
|
|
140
|
-
if type(object_) is dict:
|
|
141
|
-
return {key: self._handleObject(object_[key]) for key in object_}
|
|
142
|
-
if issubclass(object_.__class__, Evented):
|
|
143
|
-
return '__pyqtletObjectStart__{name}__pyqtletObjectEnd__'.format(name=object_.jsName)
|
|
144
|
-
if object_ is True:
|
|
145
|
-
return '__pyqtletObjectStart__true__pyqtletObjectEnd__'
|
|
146
|
-
if object_ is False:
|
|
147
|
-
return '__pyqtletObjectStart__false__pyqtletObjectEnd__'
|
|
148
|
-
if object_ is None:
|
|
149
|
-
return '__pyqtletObjectStart__null__pyqtletObjectEnd__'
|
|
150
|
-
return object_
|
|
151
|
-
|
|
152
|
-
def _qJsonValueToDict(self, object_):
|
|
153
|
-
# Qt returns QJsonValue from within the QChannel. Converting
|
|
154
|
-
# that into a dict is a small recursive function.
|
|
155
|
-
if type(object_) is QJsonValue:
|
|
156
|
-
return self._qJsonValueToDict(self._qJsonToRespectiveType(object_))
|
|
157
|
-
if type(object_) is list:
|
|
158
|
-
return [self._qJsonValueToDict(item) for item in object_]
|
|
159
|
-
if type(object_) is dict:
|
|
160
|
-
return {key: self._qJsonValueToDict(object_[key]) for key in object_}
|
|
161
|
-
return object_
|
|
162
|
-
|
|
163
|
-
def _qJsonToRespectiveType(self, object_):
|
|
164
|
-
# A QJsonValue can be one of many types. This function just
|
|
165
|
-
# converts into the correct type
|
|
166
|
-
if object_.isArray():
|
|
167
|
-
return object_.toArray()
|
|
168
|
-
if object_.isBool():
|
|
169
|
-
return object_.toBool()
|
|
170
|
-
if object_.isDouble():
|
|
171
|
-
return object_.toDouble()
|
|
172
|
-
if object_.isNull():
|
|
173
|
-
return None
|
|
174
|
-
if object_.isObject():
|
|
175
|
-
return object_.toObject()
|
|
176
|
-
if object_.isString():
|
|
177
|
-
return object_.toString()
|
|
178
|
-
if object_.isUndefined():
|
|
179
|
-
return None
|
|
180
|
-
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
from .layergroup import LayerGroup
|
|
2
|
-
from ..layer import marker, vector
|
|
3
|
-
|
|
4
|
-
class FeatureGroup(LayerGroup):
|
|
5
|
-
"""
|
|
6
|
-
Used to group several layers and handle them as one. If you add it to the map, any layers added or removed from the group will be added/removed on the map as well.
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
def _initJs(self):
|
|
10
|
-
leafletJsObject = 'new L.featureGroup()'
|
|
11
|
-
self._createJsObject(leafletJsObject, self._map.mapWidgetIndex)
|
|
12
|
-
|
|
13
|
-
def createAndAddDrawnLayer(self, drawnLayer, options=None):
|
|
14
|
-
layerType = drawnLayer['layerType']
|
|
15
|
-
if layerType == 'polygon':
|
|
16
|
-
coords = drawnLayer['layer']['_latlngs']['0']
|
|
17
|
-
coords = [coords[p] for p in coords]
|
|
18
|
-
self.addLayer(vector.Polygon(coords, options))
|
|
19
|
-
elif layerType == 'marker':
|
|
20
|
-
coords = drawnLayer['layer']['_latlng']
|
|
21
|
-
self.addLayer(marker.Marker(coords, options))
|
|
22
|
-
elif layerType == 'polyline':
|
|
23
|
-
coords = drawnLayer['layer']['_latlngs']
|
|
24
|
-
coords = [coords[p] for p in coords]
|
|
25
|
-
self.addLayer(vector.Polyline(coords, options))
|
|
26
|
-
elif layerType == 'rectangle':
|
|
27
|
-
coords = drawnLayer['layer']['_latlngs']['0']
|
|
28
|
-
coords = [coords[p] for p in coords]
|
|
29
|
-
self.addLayer(vector.Rectangle(coords, options))
|
|
30
|
-
elif layerType == 'circle':
|
|
31
|
-
coords = drawnLayer['layer']['_latlng']
|
|
32
|
-
radius = drawnLayer['layer']['options']['radius']
|
|
33
|
-
self.addLayer(vector.Circle([coords['lat'], coords['lng']], radius))
|
|
34
|
-
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from .icon import Icon
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
from ..layer import Layer
|
|
2
|
-
from ...core.Parser import Parser
|
|
3
|
-
import os
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class Icon(Layer):
|
|
7
|
-
def __init__(self, iconUrl: str, options=None):
|
|
8
|
-
super().__init__()
|
|
9
|
-
if isinstance(options, type(None)):
|
|
10
|
-
options = {}
|
|
11
|
-
self.iconUrl = iconUrl
|
|
12
|
-
self.icon_found = False
|
|
13
|
-
self.options = options
|
|
14
|
-
self._check_icon_url()
|
|
15
|
-
if self._map:
|
|
16
|
-
self._initJs()
|
|
17
|
-
|
|
18
|
-
def _check_icon_url(self):
|
|
19
|
-
if "http" in self.iconUrl:
|
|
20
|
-
self._log.info("Can't check if icon exists at url!")
|
|
21
|
-
return
|
|
22
|
-
if not os.path.isfile(self.iconUrl):
|
|
23
|
-
self._log.error(f"Can't locate file at path: '{self.iconUrl}'. Current working directory is '{os.getcwd()}'")
|
|
24
|
-
return
|
|
25
|
-
self.icon_found = True
|
|
26
|
-
|
|
27
|
-
def _initJs(self):
|
|
28
|
-
leafletJsObject = 'L.icon({options});'.format(options=Parser.dict_for_js({"iconUrl": self.iconUrl,
|
|
29
|
-
**self.options}))
|
|
30
|
-
self._createJsObject(leafletJsObject, self._map.mapWidgetIndex)
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
from .layer import Layer
|
|
2
|
-
|
|
3
|
-
class imageOverlay(Layer):
|
|
4
|
-
def __init__(self, imageURL, bounds, options=None):
|
|
5
|
-
super().__init__()
|
|
6
|
-
self.imageURL = imageURL
|
|
7
|
-
self.bounds = bounds
|
|
8
|
-
self.options = options
|
|
9
|
-
if self._map:
|
|
10
|
-
self._initJs()
|
|
11
|
-
|
|
12
|
-
def _initJs(self):
|
|
13
|
-
leafletJsObject = 'L.imageOverlay("{imageURL}",{bounds}'.format(imageURL=self.imageURL,bounds=self.bounds)
|
|
14
|
-
if self.options:
|
|
15
|
-
leafletJsObject += ', {options}'.format(options=self.options)
|
|
16
|
-
leafletJsObject += ')'
|
|
17
|
-
self._createJsObject(leafletJsObject, self._map.mapWidgetIndex)
|
|
18
|
-
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
from ..core import Evented
|
|
2
|
-
import logging
|
|
3
|
-
from abc import abstractmethod
|
|
4
|
-
|
|
5
|
-
class Layer(Evented):
|
|
6
|
-
|
|
7
|
-
# layerId is a static variable shared between all layers
|
|
8
|
-
# It is used to give unique names to layers
|
|
9
|
-
layerId = 0
|
|
10
|
-
|
|
11
|
-
@property
|
|
12
|
-
def layerName(self):
|
|
13
|
-
return self._layerName
|
|
14
|
-
|
|
15
|
-
@layerName.setter
|
|
16
|
-
def layerName(self, name):
|
|
17
|
-
self._layerName = name
|
|
18
|
-
|
|
19
|
-
@property
|
|
20
|
-
def jsName(self):
|
|
21
|
-
return self._layerName
|
|
22
|
-
|
|
23
|
-
@property
|
|
24
|
-
def map(self):
|
|
25
|
-
return self._map
|
|
26
|
-
|
|
27
|
-
@map.setter
|
|
28
|
-
def map(self, map_):
|
|
29
|
-
self._map = map_
|
|
30
|
-
|
|
31
|
-
@abstractmethod
|
|
32
|
-
def _initJs(self):
|
|
33
|
-
raise NotImplemented
|
|
34
|
-
|
|
35
|
-
def runJavaScriptForMapIndex(self, js):
|
|
36
|
-
if self._map is not None:
|
|
37
|
-
self.runJavaScript(js, self._map.mapWidgetIndex)
|
|
38
|
-
|
|
39
|
-
def getJsResponseForMapIndex(self, js, callback):
|
|
40
|
-
if self._map is not None:
|
|
41
|
-
self.getJsResponse(js, self._map.mapWidgetIndex, callback)
|
|
42
|
-
|
|
43
|
-
def __init__(self):
|
|
44
|
-
super().__init__()
|
|
45
|
-
self._map = None
|
|
46
|
-
self._layerName = self._getNewLayerName()
|
|
47
|
-
self._log = logging.getLogger(f"layer_{self._layerName}")
|
|
48
|
-
self._popup = None
|
|
49
|
-
self._popupOptions = None
|
|
50
|
-
self._tooltip = None
|
|
51
|
-
self._tooltipOptions = None
|
|
52
|
-
|
|
53
|
-
def _initPopupAndTooltip(self):
|
|
54
|
-
if self._popup is not None:
|
|
55
|
-
self._bindPopupOrTooltip("Popup", self._popup, self._popupOptions)
|
|
56
|
-
if self._tooltip is not None:
|
|
57
|
-
self._bindPopupOrTooltip("Tooltip", self._tooltip, self._tooltipOptions)
|
|
58
|
-
|
|
59
|
-
def _bindPopupOrTooltip(self, kind, content, options):
|
|
60
|
-
js = f'{self._layerName}.bind{kind}("{content}"'
|
|
61
|
-
if options is not None:
|
|
62
|
-
js += f', {self._stringifyForJs(options)}'
|
|
63
|
-
js += ')'
|
|
64
|
-
self.runJavaScriptForMapIndex(js)
|
|
65
|
-
|
|
66
|
-
def _getNewLayerName(self):
|
|
67
|
-
layerName = 'l{}'.format(self.layerId)
|
|
68
|
-
Layer.layerId += 1
|
|
69
|
-
return layerName
|
|
70
|
-
|
|
71
|
-
def addTo(self, map_):
|
|
72
|
-
map_.addLayer(self)
|
|
73
|
-
return self
|
|
74
|
-
|
|
75
|
-
def removeFrom(self, map_):
|
|
76
|
-
map_.removeLayer(self)
|
|
77
|
-
return self
|
|
78
|
-
|
|
79
|
-
def bindPopup(self, content, options=None):
|
|
80
|
-
self._popup = content
|
|
81
|
-
self._popupOptions = options
|
|
82
|
-
self._bindPopupOrTooltip("Popup", self._popup, self._popupOptions)
|
|
83
|
-
return self
|
|
84
|
-
|
|
85
|
-
def unbindPopup(self):
|
|
86
|
-
self._popup = None
|
|
87
|
-
self._popupOptions = None
|
|
88
|
-
js = '{layerName}.unbindPopup()'.format(layerName=self._layerName)
|
|
89
|
-
self.runJavaScriptForMapIndex(js)
|
|
90
|
-
return self
|
|
91
|
-
|
|
92
|
-
def bindTooltip(self, content, options=None):
|
|
93
|
-
self._tooltip = content
|
|
94
|
-
self._tooltipOptions = options
|
|
95
|
-
self._bindPopupOrTooltip("Tooltip", self._tooltip, self._tooltipOptions)
|
|
96
|
-
return self
|
|
97
|
-
|
|
98
|
-
def unbindTooltip(self):
|
|
99
|
-
self._tooltip = None
|
|
100
|
-
self._tooltipOptions = None
|
|
101
|
-
js = '{layerName}.unbindTooltip()'.format(layerName=self._layerName)
|
|
102
|
-
self.runJavaScriptForMapIndex(js)
|
|
103
|
-
return self
|
|
104
|
-
|
|
105
|
-
|