@rural-environmental-registry/map_component 1.0.0 → 1.2.0
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.
- package/README.md +444 -0
- package/dist/constants/coordinatePanel.d.ts +1 -1
- package/dist/handlers/drawingControl.d.ts +0 -1
- package/dist/handlers/mapHandler.d.ts +8 -3
- package/dist/handlers/mapToolsHandler.d.ts +39 -0
- package/dist/handlers/measureToolsHandler.d.ts +51 -0
- package/dist/handlers/memorialButtonControl.d.ts +19 -0
- package/dist/handlers/toolsConstants.d.ts +3 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.mjs +32914 -25131
- package/dist/types/index.d.ts +69 -0
- package/dist/utils/areaCalculator.d.ts +3 -0
- package/dist/utils/drawingPathOptions.d.ts +8 -0
- package/dist/utils/geometryCalculator.d.ts +13 -0
- package/dist/utils/geometryFileParseResult.d.ts +8 -0
- package/dist/utils/memorialLayer.d.ts +3 -0
- package/dist/utils/menuHistory.d.ts +8 -0
- package/dist/utils/normalizeGeoJsonToFeatures.d.ts +2 -0
- package/dist/utils/parseGeoJsonFile.d.ts +2 -0
- package/dist/utils/parseKmlFile.d.ts +2 -0
- package/dist/utils/parseShapefileZip.d.ts +3 -0
- package/dist/utils/stableMarker.d.ts +15 -0
- package/dist/utils/validateImportedPolygonGeometry.d.ts +9 -0
- package/package.json +22 -4
- package/src/assets/icons/file-import-regular-full-gray.svg +1 -0
- package/src/assets/icons/file-lines-regular-full-gray.svg +1 -0
- package/src/assets/icons/layer-icon.svg +3 -0
- package/src/assets/icons/loading.gif +0 -0
- package/src/assets/icons/marker-icon.svg +1 -0
- package/src/assets/icons/measure-icon.svg +5 -0
- package/src/assets/layers/mapLayers.json +16 -0
- package/src/assets/main.css +187 -0
- package/src/assets/styles/element-plus.css +25 -0
- package/src/assets/styles/layers-menu.css +95 -0
- package/src/assets/styles/leaflet.css +361 -0
- package/src/components/MapDPG.vue +302 -0
- package/src/components/coordinate/CoordinatePanel.vue +1206 -0
- package/src/components/fa-icon/FontAwesomeIcon.vue +44 -0
- package/src/components/loading/Loading.vue +25 -0
- package/src/components/map/LeafletMap.vue +183 -0
- package/src/components/menu/ChildMenu.vue +112 -0
- package/src/components/menu/LayerMenu.vue +294 -0
- package/src/components/menu/ParentMenu.vue +132 -0
- package/src/constants/coordinatePanel.ts +68 -0
- package/src/handlers/constants.ts +35 -0
- package/src/handlers/drawingControl.ts +143 -0
- package/src/handlers/mapHandler.ts +124 -0
- package/src/handlers/mapToolsHandler.ts +281 -0
- package/src/handlers/measureToolsHandler.ts +424 -0
- package/src/handlers/memorialButtonControl.ts +171 -0
- package/src/handlers/toolsConstants.ts +69 -0
- package/src/index.ts +6 -0
- package/src/shims-vue.d.ts +5 -0
- package/src/types/index.ts +260 -0
- package/src/types/json.d.ts +4 -0
- package/src/types/shpjs.d.ts +7 -0
- package/src/types/svg.d.ts +4 -0
- package/src/utils/CoordinateConverter.ts +43 -0
- package/src/utils/areaCalculator.ts +9 -0
- package/src/utils/drawingPathOptions.ts +57 -0
- package/src/utils/geometryCalculator.ts +62 -0
- package/src/utils/geometryFileParseResult.ts +5 -0
- package/src/utils/memorialLayer.ts +7 -0
- package/src/utils/menuHistory.ts +71 -0
- package/src/utils/normalizeGeoJsonToFeatures.ts +52 -0
- package/src/utils/parseGeoJsonFile.ts +23 -0
- package/src/utils/parseKmlFile.ts +31 -0
- package/src/utils/parseShapefileZip.ts +29 -0
- package/src/utils/stableMarker.ts +52 -0
- package/src/utils/validateImportedPolygonGeometry.ts +62 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<ElSubMenu
|
|
3
|
+
:disabled="!childrenLayers.length"
|
|
4
|
+
:index="groupData.key"
|
|
5
|
+
class="parent-menu"
|
|
6
|
+
>
|
|
7
|
+
<template #title>
|
|
8
|
+
<div class="parent-menu-row">
|
|
9
|
+
<div class="parent-layer-title">{{ groupData.name }}</div>
|
|
10
|
+
<span class="switch-component">
|
|
11
|
+
<ElSwitch
|
|
12
|
+
v-model="allLayersActive"
|
|
13
|
+
@click.stop
|
|
14
|
+
>
|
|
15
|
+
<template #active-action>
|
|
16
|
+
<FontAwesomeIcon iconName="check" />
|
|
17
|
+
</template>
|
|
18
|
+
<template #inactive-action>
|
|
19
|
+
<FontAwesomeIcon iconName="xmark" />
|
|
20
|
+
</template>
|
|
21
|
+
</ElSwitch>
|
|
22
|
+
<span class="parent-layer-status">
|
|
23
|
+
{{ allLayersActive ? groupData.toggle.active : groupData.toggle.inactive }}
|
|
24
|
+
</span>
|
|
25
|
+
<ElDivider
|
|
26
|
+
class="divider-bar"
|
|
27
|
+
direction="vertical"
|
|
28
|
+
/>
|
|
29
|
+
</span>
|
|
30
|
+
</div>
|
|
31
|
+
</template>
|
|
32
|
+
<template
|
|
33
|
+
v-for="(child, idx) in childrenLayers"
|
|
34
|
+
:key="child.key"
|
|
35
|
+
>
|
|
36
|
+
<ChildMenu
|
|
37
|
+
v-if="child.name"
|
|
38
|
+
:data="child"
|
|
39
|
+
:persist="props.persist"
|
|
40
|
+
@onChildLayerToggle="onChildChange($event, idx)"
|
|
41
|
+
/>
|
|
42
|
+
</template>
|
|
43
|
+
</ElSubMenu>
|
|
44
|
+
<ElDivider class="parent-menu-divider-row" />
|
|
45
|
+
</template>
|
|
46
|
+
|
|
47
|
+
<script lang="ts" setup>
|
|
48
|
+
import { ElDivider, ElSubMenu, ElSwitch } from 'element-plus'
|
|
49
|
+
import { computed, ref } from 'vue'
|
|
50
|
+
import FontAwesomeIcon from '../fa-icon/FontAwesomeIcon.vue'
|
|
51
|
+
import ChildMenu from './ChildMenu.vue'
|
|
52
|
+
import { GroupLayerData, LayerData } from '../../types'
|
|
53
|
+
import { resolveLayerActiveState, setHistory } from '../../utils/menuHistory.ts'
|
|
54
|
+
|
|
55
|
+
type ParentMenuProps = {
|
|
56
|
+
groupData: GroupLayerData
|
|
57
|
+
persist: boolean
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const props = defineProps<ParentMenuProps>()
|
|
61
|
+
|
|
62
|
+
const emit = defineEmits<{
|
|
63
|
+
onChildLayerToggle: [LayerData]
|
|
64
|
+
onGroupLayerToggle: [GroupLayerData]
|
|
65
|
+
}>()
|
|
66
|
+
|
|
67
|
+
const childrenLayers = ref<LayerData[]>(
|
|
68
|
+
(props.groupData.layers ?? []).map((layer) => resolveLayerActiveState(layer, props.persist))
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
const toggleVisibleAllLayers = (): void => {
|
|
72
|
+
if (props.persist) childrenLayers.value.forEach((layer: LayerData) => setHistory(layer))
|
|
73
|
+
|
|
74
|
+
emit('onGroupLayerToggle', {
|
|
75
|
+
...props.groupData,
|
|
76
|
+
layers: childrenLayers.value
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const allLayersActive = computed<boolean>({
|
|
81
|
+
get: () => childrenLayers.value.some((layer: LayerData) => layer.active),
|
|
82
|
+
set: (active: boolean) => {
|
|
83
|
+
childrenLayers.value = props.groupData.layers.map((layer: LayerData) => {
|
|
84
|
+
return {
|
|
85
|
+
...layer,
|
|
86
|
+
active
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
toggleVisibleAllLayers()
|
|
91
|
+
}
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
const onChildChange = (layer: LayerData, idx: number): void => {
|
|
95
|
+
childrenLayers.value[idx] = layer
|
|
96
|
+
emit('onChildLayerToggle', layer)
|
|
97
|
+
}
|
|
98
|
+
</script>
|
|
99
|
+
|
|
100
|
+
<style>
|
|
101
|
+
.parent-menu .parent-menu-row {
|
|
102
|
+
display: flex;
|
|
103
|
+
justify-content: space-between;
|
|
104
|
+
gap: var(--mapa-size-base-10);
|
|
105
|
+
width: 100%;
|
|
106
|
+
font-size: var(--mapa-fs-12);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.parent-menu .switch-component {
|
|
110
|
+
display: flex;
|
|
111
|
+
justify-content: center;
|
|
112
|
+
align-items: center;
|
|
113
|
+
gap: var(--mapa-size-base-5);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.parent-menu .parent-layer-title {
|
|
117
|
+
word-wrap: break-word;
|
|
118
|
+
white-space: normal;
|
|
119
|
+
line-height: 150%;
|
|
120
|
+
margin-block: auto;
|
|
121
|
+
font-weight: bold;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.parent-menu .divider-bar {
|
|
125
|
+
height: var(--mapa-size-base-20);
|
|
126
|
+
border-width: var(--mapa-size-base-2);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.parent-menu-divider-row {
|
|
130
|
+
margin: 0 !important;
|
|
131
|
+
}
|
|
132
|
+
</style>
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { CoordinatePanelTexts } from '../types'
|
|
2
|
+
|
|
3
|
+
export const COORDINATE_PANEL_TEXTS: CoordinatePanelTexts = {
|
|
4
|
+
title: 'Coordinates',
|
|
5
|
+
addPoint: 'Add Point',
|
|
6
|
+
editPoint: 'Edit Point',
|
|
7
|
+
removePoint: 'Remove Point',
|
|
8
|
+
actions: 'Actions',
|
|
9
|
+
clearGeometries: 'Clear Geometries',
|
|
10
|
+
index: 'Index',
|
|
11
|
+
x: 'X',
|
|
12
|
+
y: 'Y',
|
|
13
|
+
azimuth: 'Azimuth',
|
|
14
|
+
distance: 'Distance',
|
|
15
|
+
noPoints: 'No points added',
|
|
16
|
+
addPointTitle: 'Add Point',
|
|
17
|
+
editPointTitle: 'Edit Point',
|
|
18
|
+
removePointTitle: 'Remove Point',
|
|
19
|
+
clearGeometriesTitle: 'Clear Geometries',
|
|
20
|
+
addPointDescription: 'Add a new point to the list',
|
|
21
|
+
editPointDescription: 'Edit the selected point',
|
|
22
|
+
removePointDescription: 'Remove the selected point',
|
|
23
|
+
clearGeometriesDescription: 'Clear all geometries from the map',
|
|
24
|
+
|
|
25
|
+
memorialDescriptive: 'Descriptive Memorial',
|
|
26
|
+
referenceSystem: 'Reference System',
|
|
27
|
+
selectSystem: 'Select system',
|
|
28
|
+
sirgas2000: 'SIRGAS 2000',
|
|
29
|
+
coordinateFormat: 'Coordinate Format',
|
|
30
|
+
selectFormat: 'Select format',
|
|
31
|
+
decimalDegrees: 'Decimal Degrees (DD)',
|
|
32
|
+
degreesMinutesSeconds: 'Degrees, Minutes, Seconds (DMS)',
|
|
33
|
+
manualInput: 'Manual Input',
|
|
34
|
+
insertCoordinates: 'Insert Coordinates',
|
|
35
|
+
xLongitude: 'X (Longitude)',
|
|
36
|
+
yLatitude: 'Y (Latitude)',
|
|
37
|
+
degrees: 'Degrees',
|
|
38
|
+
minutes: 'Minutes',
|
|
39
|
+
seconds: 'Seconds',
|
|
40
|
+
addedPoints: 'Added Points',
|
|
41
|
+
finalizeGeometry: 'Finalize Geometry',
|
|
42
|
+
csvUpload: 'CSV Upload',
|
|
43
|
+
csvFileUpload: 'CSV File Upload',
|
|
44
|
+
dragCsvFile: 'Drag CSV file or click to upload',
|
|
45
|
+
csvColumnsInfo: 'The file must contain the columns: X, Y, AZIMUTH, DISTANCE',
|
|
46
|
+
applyCsvCoordinates: 'Apply CSV Coordinates',
|
|
47
|
+
|
|
48
|
+
shapefileUpload: 'Shapefile, KML and Geojson Upload',
|
|
49
|
+
shapefileFileUpload: 'Shapefile, KML and Geojson Upload',
|
|
50
|
+
dragShapefileZip: 'Drag file or click to upload',
|
|
51
|
+
shapefileZipInfo:
|
|
52
|
+
'Upload a .zip (shapefile), .kml, .geojson or .json with a single Polygon or MultiPolygon geometry',
|
|
53
|
+
shapefileAppliedSuccess: 'Geometry applied successfully',
|
|
54
|
+
geometryImportUnsupportedFormat: 'Unsupported file format. Use .zip, .kml, .geojson or .json',
|
|
55
|
+
|
|
56
|
+
placeholderLongitude: 'e.g., -46.6333',
|
|
57
|
+
placeholderLatitude: 'e.g., -23.5505',
|
|
58
|
+
placeholderAzimuth: 'e.g., 90',
|
|
59
|
+
placeholderDistance: 'e.g., 100',
|
|
60
|
+
placeholderDegrees: 'Degrees',
|
|
61
|
+
placeholderMinutes: 'Minutes',
|
|
62
|
+
placeholderSeconds: 'Seconds',
|
|
63
|
+
|
|
64
|
+
errorXYRequired: 'X and Y are required for the first point',
|
|
65
|
+
errorDegreesRequired: 'Degrees are required for the first point',
|
|
66
|
+
errorFirstRowXY: 'The first row of the CSV must contain X and Y coordinates',
|
|
67
|
+
errorProvideCoordinatesOrAzimuthDistance: 'Provide coordinates or Azimuth and Distance'
|
|
68
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { DrawingControlOptions, MapConfigConfig } from '../types'
|
|
2
|
+
|
|
3
|
+
export const DEFAULT_MAP_OPTIONS: MapConfigConfig = {
|
|
4
|
+
id: 'map',
|
|
5
|
+
zoomControl: false,
|
|
6
|
+
minZoom: 3,
|
|
7
|
+
maxZoom: 17,
|
|
8
|
+
center: [-15.235, -51.9253],
|
|
9
|
+
zoom: 4,
|
|
10
|
+
zoomControlPosition: 'topright',
|
|
11
|
+
removeControlLayers: false
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const DEFAULT_DRAW_OPTIONS: DrawingControlOptions = {
|
|
15
|
+
options: {
|
|
16
|
+
drawMarker: true,
|
|
17
|
+
drawRectangle: true,
|
|
18
|
+
drawPolyline: true,
|
|
19
|
+
drawPolygon: true,
|
|
20
|
+
drawCircle: true,
|
|
21
|
+
drawCircleMarker: true,
|
|
22
|
+
drawText: true,
|
|
23
|
+
editMode: true,
|
|
24
|
+
dragMode: true,
|
|
25
|
+
cutPolygon: false,
|
|
26
|
+
rotateMode: false,
|
|
27
|
+
removalMode: true,
|
|
28
|
+
drawControls: true,
|
|
29
|
+
editControls: true,
|
|
30
|
+
position: 'topright'
|
|
31
|
+
},
|
|
32
|
+
translation: {
|
|
33
|
+
lang: 'en'
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import L, { Map, FeatureGroup, Layer, PathOptions, Icon, Point } from 'leaflet'
|
|
2
|
+
import '@geoman-io/leaflet-geoman-free'
|
|
3
|
+
import '@geoman-io/leaflet-geoman-free/dist/leaflet-geoman.css'
|
|
4
|
+
|
|
5
|
+
import { DEFAULT_DRAW_OPTIONS } from './constants'
|
|
6
|
+
import { calculateLayerArea } from '../utils/areaCalculator'
|
|
7
|
+
import {
|
|
8
|
+
DrawingConfig,
|
|
9
|
+
GeomanDrawingEvent,
|
|
10
|
+
IncrementedLayer,
|
|
11
|
+
TranslationConfig,
|
|
12
|
+
ToolbarOptions,
|
|
13
|
+
PMToolbarOptions,
|
|
14
|
+
PMSupportedShapes
|
|
15
|
+
} from '../types'
|
|
16
|
+
|
|
17
|
+
import markerIcon from '../assets/icons/marker-icon.svg'
|
|
18
|
+
|
|
19
|
+
export default class DrawingControlHandler {
|
|
20
|
+
private readonly _map: Map
|
|
21
|
+
private readonly _drawItemsGroup: FeatureGroup
|
|
22
|
+
private readonly _options: ToolbarOptions
|
|
23
|
+
|
|
24
|
+
constructor(map: Map, drawItemsGroup: FeatureGroup, controlOptions?: DrawingConfig) {
|
|
25
|
+
this._map = map
|
|
26
|
+
this._drawItemsGroup = drawItemsGroup
|
|
27
|
+
this._options = this.formatMenuOptions(controlOptions?.options || DEFAULT_DRAW_OPTIONS.options)
|
|
28
|
+
this.addTranslation(controlOptions?.translation || DEFAULT_DRAW_OPTIONS.translation)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
get drawItemsGroup(): FeatureGroup {
|
|
32
|
+
return this._drawItemsGroup
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
get map(): Map {
|
|
36
|
+
return this._map
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
get options(): ToolbarOptions {
|
|
40
|
+
return this._options
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private incrementLayerInfos(layer: Layer): IncrementedLayer {
|
|
44
|
+
const newLayer = layer as IncrementedLayer
|
|
45
|
+
newLayer.drawnArea = calculateLayerArea(layer)
|
|
46
|
+
|
|
47
|
+
return newLayer
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public handleDrawingEvents(eventEmitterCallback: Function): void {
|
|
51
|
+
this._map.on('pm:create', (evt: GeomanDrawingEvent) => {
|
|
52
|
+
if ((this._map as L.Map & { _leafletMeasureActive?: boolean })._leafletMeasureActive) return
|
|
53
|
+
|
|
54
|
+
const { layer } = evt
|
|
55
|
+
|
|
56
|
+
this._drawItemsGroup.addLayer(layer)
|
|
57
|
+
|
|
58
|
+
const createdLayer: IncrementedLayer = this.incrementLayerInfos(layer)
|
|
59
|
+
|
|
60
|
+
eventEmitterCallback({ type: 'created', layer: createdLayer })
|
|
61
|
+
|
|
62
|
+
this.handleUpdateDrawingEvents(eventEmitterCallback, layer)
|
|
63
|
+
this.handleDeleteDrawingEvents(eventEmitterCallback, layer)
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private handleUpdateDrawingEvents(eventEmitterCallback: Function, layer: Layer): void {
|
|
68
|
+
layer.on('pm:update', (evt: GeomanDrawingEvent) => {
|
|
69
|
+
const childLayer: Layer = evt.layer
|
|
70
|
+
|
|
71
|
+
const editedLayer: IncrementedLayer = this.incrementLayerInfos(childLayer)
|
|
72
|
+
|
|
73
|
+
eventEmitterCallback({ type: 'edited', layer: editedLayer })
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
private handleDeleteDrawingEvents(eventEmitterCallback: Function, layer: Layer): void {
|
|
78
|
+
layer.on('pm:remove', (evt: GeomanDrawingEvent) => {
|
|
79
|
+
eventEmitterCallback({ type: 'deleted', layer: evt.layer })
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private addTranslation({ lang = 'en', customTexts = {} }: TranslationConfig): void {
|
|
84
|
+
this._map.pm.setLang(lang, customTexts, 'en')
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private formatMenuOptions(options: ToolbarOptions): PMToolbarOptions {
|
|
88
|
+
const toolbarOptions: ToolbarOptions = {
|
|
89
|
+
drawPolygon: false,
|
|
90
|
+
drawCircle: false,
|
|
91
|
+
drawCircleMarker: false,
|
|
92
|
+
drawText: false,
|
|
93
|
+
dragMode: false,
|
|
94
|
+
cutPolygon: false,
|
|
95
|
+
rotateMode: false
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const isPathOption = (option: any): boolean => {
|
|
99
|
+
return typeof option === 'object' && ('color' in option || 'fillColor' in option || 'weight' in option)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
Object.entries(options).forEach(([key, value]) => {
|
|
103
|
+
if (isPathOption(value)) {
|
|
104
|
+
this.applyShapeStyles(key, value as PathOptions)
|
|
105
|
+
if (key === 'drawMarker') this.setMartkerIcon()
|
|
106
|
+
toolbarOptions[key] = true
|
|
107
|
+
} else {
|
|
108
|
+
toolbarOptions[key] = value
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
return toolbarOptions
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
private applyShapeStyles(key: string, props: PathOptions): void {
|
|
116
|
+
const shapes: { [key: string]: PMSupportedShapes } = {
|
|
117
|
+
drawMarker: 'Marker',
|
|
118
|
+
drawRectangle: 'Rectangle',
|
|
119
|
+
drawPolyline: 'Line',
|
|
120
|
+
drawPolygon: 'Polygon',
|
|
121
|
+
drawCircle: 'Circle',
|
|
122
|
+
drawCircleMarker: 'CircleMarker'
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const ignoredShapes = (currentShape: PMSupportedShapes): string[] => {
|
|
126
|
+
return ['Marker', 'Circle', 'Line', 'Rectangle', 'Polygon', 'CircleMarker'].filter(
|
|
127
|
+
(shape: string) => shape !== currentShape
|
|
128
|
+
)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
this._map.pm.setPathOptions(props, { ignoreShapes: ignoredShapes(shapes[key]), merge: true })
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private setMartkerIcon(): void {
|
|
135
|
+
const MyCustomMarker = new Icon({
|
|
136
|
+
iconAnchor: new Point(12, 12),
|
|
137
|
+
iconSize: new Point(24, 24),
|
|
138
|
+
iconUrl: markerIcon
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
this._map.pm.setGlobalOptions({ markerStyle: { icon: MyCustomMarker, pane: 'markerPane' } })
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { DEFAULT_MAP_OPTIONS } from './constants'
|
|
2
|
+
import { BaseMapLayer, MapLayers, MapConfigConfig } from '../types'
|
|
3
|
+
import DEFAULT_MAP_LAYER from '../assets/layers/mapLayers.json'
|
|
4
|
+
|
|
5
|
+
import L from 'leaflet'
|
|
6
|
+
|
|
7
|
+
export default class MapHandler {
|
|
8
|
+
private readonly _map: L.Map
|
|
9
|
+
private readonly _mapOptions: MapConfigConfig
|
|
10
|
+
private readonly _initialView: { center: L.LatLngExpression; zoom: number }
|
|
11
|
+
private _mapLayers!: MapLayers
|
|
12
|
+
|
|
13
|
+
constructor(mapOptions: MapConfigConfig | undefined) {
|
|
14
|
+
this._mapOptions = {
|
|
15
|
+
...DEFAULT_MAP_OPTIONS,
|
|
16
|
+
...mapOptions
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
this._initialView = {
|
|
20
|
+
center: this._mapOptions.center!,
|
|
21
|
+
zoom: this._mapOptions.zoom!
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
this._map = L.map(this._mapOptions.id, {
|
|
25
|
+
// SVG evita desalinhamento de divIcon e geometrias efêmeras ao dar zoom
|
|
26
|
+
preferCanvas: this._mapOptions.preferCanvas ?? false,
|
|
27
|
+
markerZoomAnimation: this._mapOptions.markerZoomAnimation ?? false,
|
|
28
|
+
zoomControl: false,
|
|
29
|
+
minZoom: this._mapOptions.minZoom,
|
|
30
|
+
maxZoom: this._mapOptions.maxZoom,
|
|
31
|
+
dragging: this._mapOptions.dragging,
|
|
32
|
+
scrollWheelZoom: this._mapOptions.scrollWheelZoom,
|
|
33
|
+
doubleClickZoom: this._mapOptions.doubleClickZoom
|
|
34
|
+
}).setView(this._mapOptions.center!, this._mapOptions.zoom)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private _layerControl!: L.Control.Layers
|
|
38
|
+
|
|
39
|
+
get layerControl(): L.Control.Layers {
|
|
40
|
+
return this._layerControl
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
get map(): L.Map {
|
|
44
|
+
return this._map
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
get initialView(): { center: L.LatLngExpression; zoom: number } {
|
|
48
|
+
return this._initialView
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public init(mapLayers: MapLayers, eventEmitterCallback: Function): void {
|
|
52
|
+
this._mapLayers = mapLayers || DEFAULT_MAP_LAYER
|
|
53
|
+
|
|
54
|
+
this.addControls()
|
|
55
|
+
this.addBaseLayer(eventEmitterCallback)
|
|
56
|
+
|
|
57
|
+
setTimeout(() => {
|
|
58
|
+
this._map.invalidateSize()
|
|
59
|
+
}, 300)
|
|
60
|
+
|
|
61
|
+
this.disableLayerControlHover()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private addControls(): void {
|
|
65
|
+
if (this._mapOptions.zoomControl && this._mapOptions.zoomControlPosition) {
|
|
66
|
+
L.control.zoom({ position: this._mapOptions.zoomControlPosition }).addTo(this._map)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (!this._mapOptions.removeControlLayers) {
|
|
70
|
+
this._layerControl = L.control.layers().addTo(this._map)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
private addBaseLayer(eventEmitterCallback: Function): void {
|
|
75
|
+
const baseMap = this._mapLayers?.mapLayers
|
|
76
|
+
|
|
77
|
+
baseMap.forEach((layer: BaseMapLayer) => {
|
|
78
|
+
const tileLayer = L.tileLayer(layer.url, {
|
|
79
|
+
tms: layer.tms,
|
|
80
|
+
minZoom: layer.minZoom,
|
|
81
|
+
maxZoom: layer.maxZoom,
|
|
82
|
+
maxNativeZoom: layer.maxNativeZoom,
|
|
83
|
+
errorTileUrl: layer.errorTileUrl
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
this.watchLayerStatus(tileLayer, eventEmitterCallback)
|
|
87
|
+
|
|
88
|
+
if (layer.default) {
|
|
89
|
+
tileLayer.addTo(this._map)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
this._layerControl.addBaseLayer(tileLayer, layer.name)
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
private disableLayerControlHover(): void {
|
|
97
|
+
const container: HTMLElement | null = this._map
|
|
98
|
+
.getContainer()
|
|
99
|
+
.querySelector('.leaflet-control-layers.leaflet-control')
|
|
100
|
+
|
|
101
|
+
if (!container) return
|
|
102
|
+
|
|
103
|
+
L.DomEvent.on(container, 'click', function (e) {
|
|
104
|
+
e.stopPropagation()
|
|
105
|
+
e.preventDefault()
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
L.DomEvent.off(container)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private watchLayerStatus(tileLayer: L.TileLayer, eventEmitterCallback: Function): void {
|
|
112
|
+
tileLayer.on('loading', () => {
|
|
113
|
+
eventEmitterCallback('startLoading')
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
tileLayer.on('load', () => {
|
|
117
|
+
eventEmitterCallback('stopLoading')
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
tileLayer.on('error', () => {
|
|
121
|
+
eventEmitterCallback('stopLoading')
|
|
122
|
+
})
|
|
123
|
+
}
|
|
124
|
+
}
|