@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.
Files changed (71) hide show
  1. package/README.md +444 -0
  2. package/dist/constants/coordinatePanel.d.ts +1 -1
  3. package/dist/handlers/drawingControl.d.ts +0 -1
  4. package/dist/handlers/mapHandler.d.ts +8 -3
  5. package/dist/handlers/mapToolsHandler.d.ts +39 -0
  6. package/dist/handlers/measureToolsHandler.d.ts +51 -0
  7. package/dist/handlers/memorialButtonControl.d.ts +19 -0
  8. package/dist/handlers/toolsConstants.d.ts +3 -0
  9. package/dist/index.css +1 -1
  10. package/dist/index.d.ts +2 -0
  11. package/dist/index.mjs +32914 -25131
  12. package/dist/types/index.d.ts +69 -0
  13. package/dist/utils/areaCalculator.d.ts +3 -0
  14. package/dist/utils/drawingPathOptions.d.ts +8 -0
  15. package/dist/utils/geometryCalculator.d.ts +13 -0
  16. package/dist/utils/geometryFileParseResult.d.ts +8 -0
  17. package/dist/utils/memorialLayer.d.ts +3 -0
  18. package/dist/utils/menuHistory.d.ts +8 -0
  19. package/dist/utils/normalizeGeoJsonToFeatures.d.ts +2 -0
  20. package/dist/utils/parseGeoJsonFile.d.ts +2 -0
  21. package/dist/utils/parseKmlFile.d.ts +2 -0
  22. package/dist/utils/parseShapefileZip.d.ts +3 -0
  23. package/dist/utils/stableMarker.d.ts +15 -0
  24. package/dist/utils/validateImportedPolygonGeometry.d.ts +9 -0
  25. package/package.json +22 -4
  26. package/src/assets/icons/file-import-regular-full-gray.svg +1 -0
  27. package/src/assets/icons/file-lines-regular-full-gray.svg +1 -0
  28. package/src/assets/icons/layer-icon.svg +3 -0
  29. package/src/assets/icons/loading.gif +0 -0
  30. package/src/assets/icons/marker-icon.svg +1 -0
  31. package/src/assets/icons/measure-icon.svg +5 -0
  32. package/src/assets/layers/mapLayers.json +16 -0
  33. package/src/assets/main.css +187 -0
  34. package/src/assets/styles/element-plus.css +25 -0
  35. package/src/assets/styles/layers-menu.css +95 -0
  36. package/src/assets/styles/leaflet.css +361 -0
  37. package/src/components/MapDPG.vue +302 -0
  38. package/src/components/coordinate/CoordinatePanel.vue +1206 -0
  39. package/src/components/fa-icon/FontAwesomeIcon.vue +44 -0
  40. package/src/components/loading/Loading.vue +25 -0
  41. package/src/components/map/LeafletMap.vue +183 -0
  42. package/src/components/menu/ChildMenu.vue +112 -0
  43. package/src/components/menu/LayerMenu.vue +294 -0
  44. package/src/components/menu/ParentMenu.vue +132 -0
  45. package/src/constants/coordinatePanel.ts +68 -0
  46. package/src/handlers/constants.ts +35 -0
  47. package/src/handlers/drawingControl.ts +143 -0
  48. package/src/handlers/mapHandler.ts +124 -0
  49. package/src/handlers/mapToolsHandler.ts +281 -0
  50. package/src/handlers/measureToolsHandler.ts +424 -0
  51. package/src/handlers/memorialButtonControl.ts +171 -0
  52. package/src/handlers/toolsConstants.ts +69 -0
  53. package/src/index.ts +6 -0
  54. package/src/shims-vue.d.ts +5 -0
  55. package/src/types/index.ts +260 -0
  56. package/src/types/json.d.ts +4 -0
  57. package/src/types/shpjs.d.ts +7 -0
  58. package/src/types/svg.d.ts +4 -0
  59. package/src/utils/CoordinateConverter.ts +43 -0
  60. package/src/utils/areaCalculator.ts +9 -0
  61. package/src/utils/drawingPathOptions.ts +57 -0
  62. package/src/utils/geometryCalculator.ts +62 -0
  63. package/src/utils/geometryFileParseResult.ts +5 -0
  64. package/src/utils/memorialLayer.ts +7 -0
  65. package/src/utils/menuHistory.ts +71 -0
  66. package/src/utils/normalizeGeoJsonToFeatures.ts +52 -0
  67. package/src/utils/parseGeoJsonFile.ts +23 -0
  68. package/src/utils/parseKmlFile.ts +31 -0
  69. package/src/utils/parseShapefileZip.ts +29 -0
  70. package/src/utils/stableMarker.ts +52 -0
  71. 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
+ }