@mapcreator/sdk 0.0.8 → 0.0.9

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 (123) hide show
  1. package/dist/{HighlightManager.d.ts → esm/HighlightManager.d.ts} +2 -2
  2. package/dist/esm/HighlightManager.js +203 -0
  3. package/dist/{MCMap.d.ts → esm/MCMap.d.ts} +1 -1
  4. package/dist/esm/MCMap.js +254 -0
  5. package/dist/{PopupManager.d.ts → esm/PopupManager.d.ts} +4 -4
  6. package/dist/esm/PopupManager.js +297 -0
  7. package/dist/{Registry.d.ts → esm/Registry.d.ts} +3 -3
  8. package/dist/esm/Registry.js +74 -0
  9. package/dist/{adornments → esm/adornments}/categoricalLegend.d.ts +1 -1
  10. package/dist/esm/adornments/categoricalLegend.js +141 -0
  11. package/dist/{adornments → esm/adornments}/connectedLegend.d.ts +2 -2
  12. package/dist/esm/adornments/connectedLegend.js +393 -0
  13. package/dist/{adornments → esm/adornments}/customAdornment.d.ts +1 -1
  14. package/dist/esm/adornments/customAdornment.js +29 -0
  15. package/dist/{adornments → esm/adornments}/heading.d.ts +1 -1
  16. package/dist/esm/adornments/heading.js +71 -0
  17. package/dist/esm/adornments/insetMap.d.ts +3 -0
  18. package/dist/esm/adornments/insetMap.js +351 -0
  19. package/dist/{adornments → esm/adornments}/manualLegend.d.ts +1 -1
  20. package/dist/esm/adornments/manualLegend.js +15 -0
  21. package/dist/esm/adornments/northArrow.d.ts +3 -0
  22. package/dist/esm/adornments/northArrow.js +24 -0
  23. package/dist/esm/adornments/scalebar.d.ts +3 -0
  24. package/dist/esm/adornments/scalebar.js +176 -0
  25. package/dist/{constants → esm/constants}/index.d.ts +2 -2
  26. package/dist/esm/constants/index.js +53 -0
  27. package/dist/esm/controls/controls.js +7 -0
  28. package/dist/{controls → esm/controls}/fullscreenControls.d.ts +1 -1
  29. package/dist/esm/controls/fullscreenControls.js +29 -0
  30. package/dist/{controls → esm/controls}/geocoderControl.d.ts +1 -1
  31. package/dist/esm/controls/geocoderControl.js +202 -0
  32. package/dist/{controls → esm/controls}/geolocationControls.d.ts +1 -1
  33. package/dist/esm/controls/geolocationControls.js +65 -0
  34. package/dist/esm/controls/refreshMapControls.d.ts +3 -0
  35. package/dist/esm/controls/refreshMapControls.js +26 -0
  36. package/dist/esm/controls/webControls.d.ts +4 -0
  37. package/dist/esm/controls/webControls.js +40 -0
  38. package/dist/{controls → esm/controls}/zoomControls.d.ts +1 -1
  39. package/dist/esm/controls/zoomControls.js +23 -0
  40. package/dist/esm/i18n.js +21 -0
  41. package/dist/esm/index.d.ts +5 -0
  42. package/dist/esm/index.js +5 -0
  43. package/dist/esm/locales/da_DK/strings.json +7 -0
  44. package/dist/esm/locales/de_DE/strings.json +7 -0
  45. package/dist/esm/locales/en_GB/strings.json +7 -0
  46. package/dist/esm/locales/es_ES/strings.json +7 -0
  47. package/dist/esm/locales/fr_FR/strings.json +7 -0
  48. package/dist/esm/locales/it_IT/strings.json +7 -0
  49. package/dist/esm/locales/nl_NL/strings.json +7 -0
  50. package/dist/esm/models/area.d.ts +5 -0
  51. package/dist/esm/models/area.js +165 -0
  52. package/dist/esm/models/circle.d.ts +5 -0
  53. package/dist/esm/models/circle.js +110 -0
  54. package/dist/esm/models/dot.d.ts +3 -0
  55. package/dist/esm/models/dot.js +42 -0
  56. package/dist/esm/models/line.d.ts +4 -0
  57. package/dist/esm/models/line.js +117 -0
  58. package/dist/esm/models/marker.d.ts +5 -0
  59. package/dist/esm/models/marker.js +179 -0
  60. package/dist/esm/models/polygon.d.ts +5 -0
  61. package/dist/esm/models/polygon.js +80 -0
  62. package/dist/{renderAdornments.d.ts → esm/renderAdornments.d.ts} +3 -3
  63. package/dist/esm/renderAdornments.js +129 -0
  64. package/dist/esm/types/geometry.js +1 -0
  65. package/dist/{types → esm/types}/index.d.ts +1 -1
  66. package/dist/esm/types/index.js +1 -0
  67. package/dist/esm/types/jobObject.js +1 -0
  68. package/dist/{types → esm/types}/mapstyle.d.ts +6 -2
  69. package/dist/esm/types/mapstyle.js +1 -0
  70. package/dist/esm/utils/browser.js +6 -0
  71. package/dist/{utils → esm/utils}/choropleth.d.ts +3 -3
  72. package/dist/esm/utils/choropleth.js +110 -0
  73. package/dist/esm/utils/fullscreen.js +40 -0
  74. package/dist/{utils → esm/utils}/geolocation.d.ts +1 -1
  75. package/dist/esm/utils/geolocation.js +93 -0
  76. package/dist/{utils → esm/utils}/graphhopper.d.ts +1 -1
  77. package/dist/esm/utils/graphhopper.js +41 -0
  78. package/dist/{utils → esm/utils}/helpers.d.ts +2 -2
  79. package/dist/esm/utils/helpers.js +116 -0
  80. package/dist/{utils → esm/utils}/language.d.ts +1 -1
  81. package/dist/esm/utils/language.js +170 -0
  82. package/dist/{utils → esm/utils}/models.d.ts +4 -4
  83. package/dist/esm/utils/models.js +103 -0
  84. package/dist/{utils → esm/utils}/overlays.d.ts +1 -1
  85. package/dist/esm/utils/overlays.js +87 -0
  86. package/dist/esm/utils/scalebar.js +52 -0
  87. package/dist/{utils → esm/utils}/svgHelpers.d.ts +4 -3
  88. package/dist/esm/utils/svgHelpers.js +1512 -0
  89. package/dist/{utils → esm/utils}/template.d.ts +2 -2
  90. package/dist/esm/utils/template.js +120 -0
  91. package/dist/{utils → esm/utils}/youtube.d.ts +1 -1
  92. package/dist/esm/utils/youtube.js +64 -0
  93. package/dist/favicon-32x32.png +0 -0
  94. package/dist/mapcreator-sdk.umd.cjs +3 -3
  95. package/dist/report.html +4950 -0
  96. package/package.json +7 -7
  97. package/dist/adornments/insetMap.d.ts +0 -3
  98. package/dist/adornments/northArrow.d.ts +0 -3
  99. package/dist/adornments/scalebar.d.ts +0 -3
  100. package/dist/controls/refreshMapControls.d.ts +0 -3
  101. package/dist/controls/webControls.d.ts +0 -4
  102. package/dist/index.d.ts +0 -3
  103. package/dist/locales/da_DK/strings.json.d.ts +0 -10
  104. package/dist/locales/de_DE/strings.json.d.ts +0 -10
  105. package/dist/locales/en_GB/strings.json.d.ts +0 -10
  106. package/dist/locales/es_ES/strings.json.d.ts +0 -10
  107. package/dist/locales/fr_FR/strings.json.d.ts +0 -10
  108. package/dist/locales/it_IT/strings.json.d.ts +0 -10
  109. package/dist/locales/nl_NL/strings.json.d.ts +0 -10
  110. package/dist/mapcreator-sdk.js +0 -39590
  111. package/dist/models/area.d.ts +0 -5
  112. package/dist/models/circle.d.ts +0 -5
  113. package/dist/models/dot.d.ts +0 -3
  114. package/dist/models/line.d.ts +0 -4
  115. package/dist/models/marker.d.ts +0 -5
  116. package/dist/models/polygon.d.ts +0 -5
  117. /package/dist/{controls → esm/controls}/controls.d.ts +0 -0
  118. /package/dist/{i18n.d.ts → esm/i18n.d.ts} +0 -0
  119. /package/dist/{types → esm/types}/geometry.d.ts +0 -0
  120. /package/dist/{types → esm/types}/jobObject.d.ts +0 -0
  121. /package/dist/{utils → esm/utils}/browser.d.ts +0 -0
  122. /package/dist/{utils → esm/utils}/fullscreen.d.ts +0 -0
  123. /package/dist/{utils → esm/utils}/scalebar.d.ts +0 -0
@@ -0,0 +1,351 @@
1
+ import { area } from '@turf/area';
2
+ import { coordEach } from '@turf/meta';
3
+ import { feature } from 'topojson-client';
4
+ import { intersect } from '@turf/intersect';
5
+ import { lerp } from '@/utils/helpers';
6
+ import { featureCollection, lineString, polygon } from '@turf/helpers';
7
+ import { geoPath, geoMercator, geoGraticule, geoOrthographic, } from 'd3-geo';
8
+ // Inset map size at scale=1
9
+ const baseSize = 100;
10
+ export function useInsetMap(insetMap, map, cdnUrl) {
11
+ const container = document.createElement('div');
12
+ container.className = 'adornment';
13
+ const scale = insetMap.scale ?? 1;
14
+ const size = scale * baseSize;
15
+ container.style.width = `${size}px`;
16
+ container.style.height = `${size}px`;
17
+ container.style.flexGrow = '0';
18
+ container.style.flexShrink = '0';
19
+ // Creating context
20
+ const context = {
21
+ id: insetMap.id,
22
+ map,
23
+ container,
24
+ moveEndPosition: map.getCenter(),
25
+ geoJson: undefined,
26
+ isGlobe: insetMap.isGlobe,
27
+ boundingBox: insetMap.boundingBox,
28
+ scale,
29
+ backgroundColor: insetMap.backgroundColor ?? '#000000',
30
+ globeBackgroundColor: insetMap.globeBackgroundColor ?? '#000000',
31
+ borderColor: insetMap.borderColor ?? '#000000',
32
+ primaryFillColor: insetMap.primaryFillColor ?? '#000000',
33
+ primaryStrokeColor: insetMap.primaryStrokeColor ?? '#000000',
34
+ secondaryFillColor: insetMap.secondaryFillColor ?? '#000000',
35
+ secondaryStrokeColor: insetMap.secondaryStrokeColor ?? '#000000',
36
+ graticuleColor: insetMap.graticuleColor ?? '#000000',
37
+ frameColor: insetMap.frameColor ?? '#000000',
38
+ frameOutlineColor: insetMap.frameOutlineColor ?? '#000000',
39
+ dotColor: insetMap.dotColor ?? '#000000',
40
+ dotOutlineColor: insetMap.dotOutlineColor ?? '#000000',
41
+ globeFallbackActive: false,
42
+ globeGeoJsonRequested: false,
43
+ globeGeoJson: undefined,
44
+ };
45
+ // Initializing data download
46
+ fetch(`${cdnUrl}/data/insetmaps/${insetMap.fileName}`)
47
+ .then(response => response.json())
48
+ .then(topology => {
49
+ // Once downloaded, add data to context and rerender
50
+ context.geoJson = feature(topology, Object.keys(topology.objects)[0]);
51
+ render(context);
52
+ });
53
+ map.on('moveend', () => {
54
+ context.moveEndPosition = map.getCenter();
55
+ // Only do a full rerender at moveend if we're rendering a globe
56
+ if (context.isGlobe || context.globeFallbackActive) {
57
+ render(context);
58
+ }
59
+ });
60
+ map.on('move', () => {
61
+ // Non-globe inset map: fallback to globe logic
62
+ if (!context.isGlobe) {
63
+ const [minLng, minLat, maxLng, maxLat] = context.boundingBox;
64
+ const frameGeometry = calcAspectBoxGeometry(map);
65
+ const insetMapGeometry = polygon([
66
+ [
67
+ [minLng, minLat],
68
+ [minLng, maxLat],
69
+ [maxLng, maxLat],
70
+ [maxLng, minLat],
71
+ [minLng, minLat],
72
+ ],
73
+ ]);
74
+ const intersection = intersect(featureCollection([frameGeometry, insetMapGeometry]));
75
+ const frameArea = area(frameGeometry);
76
+ const intersectionArea = intersection ? area(intersection) : 0;
77
+ // We consider to be out of inset map bounds if more than 25% of the frame is outside of the map
78
+ const outOfBounds = intersectionArea < frameArea * 0.75;
79
+ // Fallback turned on or off: full rerender
80
+ if (context.globeFallbackActive !== outOfBounds) {
81
+ context.globeFallbackActive = outOfBounds;
82
+ context.moveEndPosition = map.getCenter();
83
+ render(context);
84
+ }
85
+ // Request fallback globe data if needed
86
+ if (context.globeFallbackActive && !context.globeGeoJsonRequested) {
87
+ context.globeGeoJsonRequested = true;
88
+ fetch(`${cdnUrl}/data/insetmaps/world-110m.json`)
89
+ .then(response => response.json())
90
+ .then(topology => {
91
+ // Once downloaded, do a full rerender
92
+ context.globeGeoJson = feature(topology, Object.keys(topology.objects)[0]);
93
+ context.moveEndPosition = map.getCenter();
94
+ render(context);
95
+ });
96
+ }
97
+ }
98
+ // Always update frame highlight on move
99
+ positionHighlight(context);
100
+ });
101
+ return container;
102
+ }
103
+ /**
104
+ * Full rerender of the inset map
105
+ */
106
+ function render(context) {
107
+ const { id, container, scale, backgroundColor, globeBackgroundColor, secondaryFillColor, secondaryStrokeColor, primaryFillColor, primaryStrokeColor, graticuleColor, borderColor, frameColor, frameOutlineColor, dotColor, dotOutlineColor, } = context;
108
+ // Decide if we’re rendering globe as fallback
109
+ const globeFallback = context.globeFallbackActive && context.globeGeoJson !== undefined;
110
+ const geoJson = globeFallback ? context.globeGeoJson : context.geoJson;
111
+ const isGlobe = globeFallback || context.isGlobe;
112
+ // Data not downloaded yet
113
+ if (geoJson === undefined) {
114
+ return;
115
+ }
116
+ const size = baseSize * scale;
117
+ const halfSize = size / 2;
118
+ const lineWidth = 0.1 * scale;
119
+ // Background nodes
120
+ const nodes = [
121
+ isGlobe
122
+ ? `<circle cx="${halfSize}" cy="${halfSize}" r="${halfSize}" fill="${globeBackgroundColor}"></circle>`
123
+ : `<rect x="0" y="0" width="${size}" height="${size}" fill="${backgroundColor}"></rect>`,
124
+ ];
125
+ const { path } = getPathAndProjection(context);
126
+ // Secondary polygons
127
+ const dSecondary = path({
128
+ ...geoJson,
129
+ features: geoJson.features.filter(f => f.properties?.class !== 'P1'),
130
+ });
131
+ if (dSecondary) {
132
+ nodes.push(`
133
+ <path
134
+ d="${dSecondary}"
135
+ fill="${secondaryFillColor}"
136
+ stroke="${secondaryStrokeColor}"
137
+ stroke-width="${lineWidth}"
138
+ ></path>
139
+ `);
140
+ }
141
+ // Primary polygons
142
+ const dPrimary = path({
143
+ ...geoJson,
144
+ features: geoJson.features.filter(f => f.properties?.class === 'P1'),
145
+ });
146
+ if (dPrimary) {
147
+ nodes.push(`
148
+ <path
149
+ d="${dPrimary}"
150
+ fill="${primaryFillColor}"
151
+ stroke="${primaryStrokeColor}"
152
+ stroke-width="${lineWidth}"
153
+ ></path>
154
+ `);
155
+ }
156
+ // Graticules (for globe)
157
+ if (isGlobe) {
158
+ const dGraticule = path(featureCollection(geoGraticule()
159
+ .step([10, 10])
160
+ .lines()
161
+ .map(geometry => lineString(geometry.coordinates))));
162
+ nodes.push(`
163
+ <path
164
+ d="${dGraticule}"
165
+ fill="none"
166
+ stroke="${graticuleColor}"
167
+ stroke-width="${lineWidth}"
168
+ ></path>
169
+ `);
170
+ }
171
+ // Frame highlight nodes. Geometries and visibility will be set in positionHighlight
172
+ nodes.push(`
173
+ <path
174
+ id="frame-outline-${id}"
175
+ fill="none"
176
+ stroke="${frameOutlineColor}"
177
+ stroke-width="${4 * scale}"
178
+ ></path>
179
+ `);
180
+ nodes.push(`
181
+ <path
182
+ id="frame-${id}"
183
+ fill="none"
184
+ stroke="${frameColor}"
185
+ stroke-width="${2 * scale}"
186
+ ></path>
187
+ `);
188
+ nodes.push(`
189
+ <circle
190
+ id="dot-${id}"
191
+ r="${5 * scale}"
192
+ fill="${dotColor}"
193
+ stroke="${dotOutlineColor}"
194
+ stroke-width="${2 * scale}"
195
+ ></circle>
196
+ `);
197
+ // Inset map border (non-globe only)
198
+ if (!isGlobe) {
199
+ nodes.push(`
200
+ <rect
201
+ x="${scale / 2}"
202
+ y="${scale / 2}"
203
+ width="${size - scale}"
204
+ height="${size - scale}"
205
+ fill="none"
206
+ stroke="${borderColor}"
207
+ stroke-width="${scale}"
208
+ ></rect>
209
+ `);
210
+ }
211
+ const svg = `
212
+ <svg
213
+ xmlns="http://www.w3.org/2000/svg"
214
+ viewBox="0 0 ${size} ${size}"
215
+ width="${size}"
216
+ height="${size}"
217
+ >
218
+ ${nodes.join('')}
219
+ </svg>
220
+ `;
221
+ container.innerHTML = svg;
222
+ positionHighlight(context);
223
+ }
224
+ /**
225
+ * Calculates D3 Projection and Path generator
226
+ */
227
+ function getPathAndProjection(context) {
228
+ const { scale, boundingBox, moveEndPosition } = context;
229
+ // Decide if we’re rendering a globe
230
+ const globeFallback = context.globeFallbackActive && context.globeGeoJson !== undefined;
231
+ const isGlobe = globeFallback || context.isGlobe;
232
+ const size = baseSize * scale;
233
+ const path = geoPath();
234
+ let projection;
235
+ if (isGlobe) {
236
+ const { lng, lat } = moveEndPosition;
237
+ projection = geoOrthographic()
238
+ .scale(size / 2)
239
+ .translate([size / 2, size / 2])
240
+ .rotate([-lng, -lat])
241
+ .clipAngle(90);
242
+ }
243
+ else {
244
+ projection = geoMercator().scale(1).translate([0, 0]);
245
+ const topLeft = projection([boundingBox[0], boundingBox[3]]);
246
+ const bottomRight = projection([boundingBox[2], boundingBox[1]]);
247
+ const dx = Math.abs(bottomRight[0] - topLeft[0]);
248
+ const dy = Math.abs(bottomRight[1] - topLeft[1]);
249
+ const x = (topLeft[0] + bottomRight[0]) / 2;
250
+ const y = (topLeft[1] + bottomRight[1]) / 2;
251
+ const scaleFactor = 1 / Math.max(dx / size, dy / size);
252
+ projection
253
+ .scale(scaleFactor)
254
+ .translate([size / 2 - scaleFactor * x, size / 2 - scaleFactor * y]);
255
+ }
256
+ path.projection(projection);
257
+ return { path, projection };
258
+ }
259
+ /**
260
+ * Updates frame highlight nodes, called on every frame
261
+ */
262
+ function positionHighlight(context) {
263
+ const { id, map, container, scale } = context;
264
+ const frameNode = container.querySelector(`#frame-${id}`);
265
+ const frameOutlineNode = container.querySelector(`#frame-outline-${id}`);
266
+ const dotNode = container.querySelector(`#dot-${id}`);
267
+ // Map not rendered yet (waiting for data)
268
+ if (!frameNode || !frameOutlineNode || !dotNode) {
269
+ return;
270
+ }
271
+ const frameGeometry = calcAspectBoxGeometry(map);
272
+ const { path, projection } = getPathAndProjection(context);
273
+ let minLng = Infinity;
274
+ let maxLng = -Infinity;
275
+ let minX = Infinity;
276
+ let maxX = -Infinity;
277
+ let minY = Infinity;
278
+ let maxY = -Infinity;
279
+ coordEach(frameGeometry, coord => {
280
+ const point = projection(coord);
281
+ minLng = Math.min(minLng, coord[0]);
282
+ maxLng = Math.max(maxLng, coord[0]);
283
+ minX = Math.min(minX, point[0]);
284
+ maxX = Math.max(maxX, point[0]);
285
+ minY = Math.min(minY, point[1]);
286
+ maxY = Math.max(maxY, point[1]);
287
+ });
288
+ const dotThreshold = 10 * scale;
289
+ if (maxX - minX < dotThreshold || maxY - minY < dotThreshold) {
290
+ // Frame to small: rendering as circle
291
+ frameNode.style.display = 'none';
292
+ frameOutlineNode.style.display = 'none';
293
+ dotNode.style.display = 'block';
294
+ const center = map.getCenter();
295
+ const [x, y] = projection([center.lng, center.lat]);
296
+ dotNode.setAttribute('cx', String(x));
297
+ dotNode.setAttribute('cy', String(y));
298
+ }
299
+ else if (maxLng - minLng < 180) {
300
+ // Frame not to big: rendering as polygon
301
+ frameNode.style.display = 'block';
302
+ frameOutlineNode.style.display = 'block';
303
+ dotNode.style.display = 'none';
304
+ const d = path(frameGeometry);
305
+ frameNode.setAttribute('d', d);
306
+ frameOutlineNode.setAttribute('d', d);
307
+ }
308
+ else {
309
+ // Frame too large: not rendering at all
310
+ frameNode.style.display = 'none';
311
+ frameOutlineNode.style.display = 'none';
312
+ dotNode.style.display = 'none';
313
+ }
314
+ }
315
+ /**
316
+ * Calculates GeoJSON Polygon geometry of the map viewport
317
+ */
318
+ function calcAspectBoxGeometry(map) {
319
+ const leftX = 0;
320
+ const topY = 0;
321
+ const rightX = map.getCanvas().width;
322
+ const bottomY = map.getCanvas().height;
323
+ const precision = 64;
324
+ const coordinates = [];
325
+ for (let i = 0; i < precision; i++) {
326
+ const { lng, lat } = map.unproject([lerp(leftX, rightX, i / precision), topY]);
327
+ coordinates.push([lng, lat]);
328
+ }
329
+ for (let i = 0; i < precision; i++) {
330
+ const { lng, lat } = map.unproject([rightX, lerp(topY, bottomY, i / precision)]);
331
+ coordinates.push([lng, lat]);
332
+ }
333
+ for (let i = 0; i < precision; i++) {
334
+ const { lng, lat } = map.unproject([lerp(rightX, leftX, i / precision), bottomY]);
335
+ coordinates.push([lng, lat]);
336
+ }
337
+ for (let i = 0; i < precision; i++) {
338
+ const { lng, lat } = map.unproject([leftX, lerp(bottomY, topY, i / precision)]);
339
+ coordinates.push([lng, lat]);
340
+ }
341
+ const { lng, lat } = map.unproject([leftX, topY]);
342
+ coordinates.push([lng, lat]);
343
+ return {
344
+ type: 'Feature',
345
+ properties: {},
346
+ geometry: {
347
+ coordinates: [coordinates],
348
+ type: 'Polygon',
349
+ },
350
+ };
351
+ }
@@ -1,2 +1,2 @@
1
- import { JobObjectManualLegend } from '../types/jobObject';
1
+ import type { JobObjectManualLegend } from '@/types/jobObject';
2
2
  export declare function useManualLegend(legend: JobObjectManualLegend, vapiUrl: string, accessToken: string): HTMLElement;
@@ -0,0 +1,15 @@
1
+ import { loadFonts } from '@/utils/svgHelpers';
2
+ import { getCategoricalLegendSvg } from '@/adornments/categoricalLegend';
3
+ export function useManualLegend(legend, vapiUrl, accessToken) {
4
+ const container = document.createElement('div');
5
+ container.className = 'adornment';
6
+ if (legend.showBackground) {
7
+ container.style.boxShadow = '0 0 7px 0 rgba(33, 37, 41, 0.25)';
8
+ container.style.borderRadius = '10px';
9
+ }
10
+ const defaultFont = 'ArialMT';
11
+ const fonts = [legend.titleFont ?? defaultFont, legend.entryFont ?? defaultFont];
12
+ const fontsToLoad = Array.from(new Set(fonts));
13
+ loadFonts(fontsToLoad, vapiUrl, accessToken).then(() => (container.innerHTML = getCategoricalLegendSvg(legend, defaultFont)));
14
+ return container;
15
+ }
@@ -0,0 +1,3 @@
1
+ import type { Map } from '@mapcreator/maplibre-gl';
2
+ import type { JobObjectNorthArrow } from '@/types/jobObject';
3
+ export declare function useNorthArrow(northArrow: JobObjectNorthArrow, map: Map): HTMLElement;
@@ -0,0 +1,24 @@
1
+ export function useNorthArrow(northArrow, map) {
2
+ const container = document.createElement('div');
3
+ container.className = 'adornment';
4
+ const size = (northArrow.scale ?? 1) * 30;
5
+ container.style.cssText = `width: ${size}px; height: ${size}px`;
6
+ map.on('move', () => {
7
+ container.innerHTML = getSvg(map, size);
8
+ });
9
+ container.innerHTML = getSvg(map, size);
10
+ return container;
11
+ }
12
+ function getSvg(map, size) {
13
+ const rotation = map.getBearing();
14
+ const pitch = map.getPitch();
15
+ return `
16
+ <svg width="${size}" height="${size}" viewBox="-5 -5 30 30" xmlns="http://www.w3.org/2000/svg">
17
+ <g class="tilt" style="transform-origin: 33% 33%; transform: rotateX(${pitch}deg);">
18
+ <g class="rotate" style="transform-origin: 33% 33%; transform: rotate(${-rotation}deg);">
19
+ <path d="M0 20L10 0L20 20L10 12.7711L0 20Z" fill="#343A40" />
20
+ </g>
21
+ </g>
22
+ </svg>
23
+ `;
24
+ }
@@ -0,0 +1,3 @@
1
+ import type { Map } from '@mapcreator/maplibre-gl';
2
+ import type { JobObjectScalebar } from '@/types/jobObject';
3
+ export declare function useScalebar(scalebar: JobObjectScalebar, map: Map): HTMLElement;
@@ -0,0 +1,176 @@
1
+ import { calcDistance, unitConvert } from '@/utils/helpers';
2
+ import { floatToFraction, getRoundNum } from '@/utils/scalebar';
3
+ const units = {
4
+ kilometer: 'km',
5
+ meter: 'm',
6
+ mile: 'mi',
7
+ foot: 'ft',
8
+ nauticalMile: 'nm',
9
+ };
10
+ export function useScalebar(scalebar, map) {
11
+ const container = document.createElement('div');
12
+ container.className = 'adornment';
13
+ container.innerHTML = getSvg(map, scalebar);
14
+ map.on('move', () => {
15
+ container.innerHTML = getSvg(map, scalebar);
16
+ });
17
+ return container;
18
+ }
19
+ function getSvg(map, scalebar) {
20
+ const { maxWidth = 100, fontSize = 8, font = 'Arial, sans-serif', color = '#000000', unit = 'metric_and_imperial', } = scalebar;
21
+ const center = map.getCenter();
22
+ const point = map.project(center);
23
+ const maxMeters = calcDistance(center, map.unproject([point.x + maxWidth, point.y]));
24
+ const scaleData = Object.fromEntries(Object.entries(units).map(([u, abbr]) => {
25
+ const value = unitConvert('meter', u, maxMeters);
26
+ const dist = getRoundNum(value) || 0;
27
+ const ratio = value ? dist / value : 0;
28
+ const humanized = dist < 1 ? `${floatToFraction(dist).join('/')} ${abbr}` : `${dist} ${abbr}`;
29
+ return [u, { value, ratio, humanized }];
30
+ }));
31
+ const scaleMetric = scaleData.meter.value > 1000 ? scaleData.kilometer : scaleData.meter;
32
+ const scaleImperial = scaleData.foot.value > 5280 ? scaleData.mile : scaleData.foot;
33
+ const scaleMap = {
34
+ imperial: scaleImperial,
35
+ metric: scaleMetric,
36
+ nautical: scaleData.nauticalMile,
37
+ };
38
+ const scaleSingle = unit !== 'metric_and_imperial' ? scaleMap[unit] : undefined;
39
+ const scaleLengths = {
40
+ metric: Math.round(maxWidth * scaleMetric.ratio),
41
+ imperial: Math.round(maxWidth * scaleImperial.ratio),
42
+ };
43
+ const mainBarLength = unit === 'metric_and_imperial'
44
+ ? Math.max(scaleLengths.metric, scaleLengths.imperial)
45
+ : Math.round(maxWidth * (scaleSingle?.ratio ?? 0));
46
+ const baseY = fontSize + 7;
47
+ if (unit === 'metric_and_imperial') {
48
+ return `
49
+ <svg
50
+ xmlns="http://www.w3.org/2000/svg"
51
+ width="${mainBarLength}"
52
+ height="${baseY + fontSize * 2}"
53
+ >
54
+ <line
55
+ x1="0"
56
+ y1="${baseY - 0.5}"
57
+ x2="${mainBarLength}"
58
+ y2="${baseY - 0.5}"
59
+ stroke="${color}"
60
+ stroke-width="1"
61
+ />
62
+ <line
63
+ x1="0.5"
64
+ y1="${baseY - 5}"
65
+ x2="0.5"
66
+ y2="${baseY + 5}"
67
+ stroke="${color}"
68
+ stroke-width="1"
69
+ />
70
+ <line
71
+ x1="${scaleLengths.imperial - 0.5}"
72
+ y1="${baseY - 5}"
73
+ x2="${scaleLengths.imperial - 0.5}"
74
+ y2="${baseY}"
75
+ stroke="${color}"
76
+ stroke-width="1"
77
+ />
78
+ <line
79
+ x1="${scaleLengths.metric - 0.5}"
80
+ y1="${baseY}"
81
+ x2="${scaleLengths.metric - 0.5}"
82
+ y2="${baseY + 5}"
83
+ stroke="${color}"
84
+ stroke-width="1"
85
+ />
86
+ <text
87
+ x="0"
88
+ y="${baseY + 7 + fontSize}"
89
+ font-size="${fontSize}px"
90
+ font-family="${font}"
91
+ font-weight="bold"
92
+ fill="${color}"
93
+ text-anchor="start"
94
+ >
95
+ 0
96
+ </text>
97
+ <text
98
+ x="${scaleLengths.imperial}"
99
+ y="${baseY - 7}"
100
+ font-size="${fontSize}px"
101
+ font-family="${font}"
102
+ font-weight="bold"
103
+ fill="${color}"
104
+ text-anchor="end"
105
+ >
106
+ ${scaleImperial.humanized}
107
+ </text>
108
+ <text
109
+ x="${scaleLengths.metric}"
110
+ y="${baseY + 7 + fontSize}"
111
+ font-size="${fontSize}px"
112
+ font-family="${font}"
113
+ font-weight="bold"
114
+ fill="${color}"
115
+ text-anchor="end"
116
+ >
117
+ ${scaleMetric.humanized}
118
+ </text>
119
+ </svg>
120
+ `;
121
+ }
122
+ return `
123
+ <svg
124
+ xmlns="http://www.w3.org/2000/svg"
125
+ width="${mainBarLength}"
126
+ height="${baseY + fontSize + 5}"
127
+ >
128
+ <line
129
+ x1="0"
130
+ y1="4.5"
131
+ x2="${mainBarLength}"
132
+ y2="4.5"
133
+ stroke="${color}"
134
+ stroke-width="1"
135
+ />
136
+ <line
137
+ x1="0.5"
138
+ y1="0"
139
+ x2="0.5"
140
+ y2="10"
141
+ stroke="${color}"
142
+ stroke-width="1"
143
+ />
144
+ <line
145
+ x1="${mainBarLength - 0.5}"
146
+ y1="0"
147
+ x2="${mainBarLength - 0.5}"
148
+ y2="10"
149
+ stroke="${color}"
150
+ stroke-width="1"
151
+ />
152
+ <text
153
+ x="0"
154
+ y="${fontSize + 12}"
155
+ font-size="${fontSize}px"
156
+ font-family="${font}"
157
+ font-weight="bold"
158
+ fill="${color}"
159
+ text-anchor="start"
160
+ >
161
+ 0
162
+ </text>
163
+ <text
164
+ x="${mainBarLength}"
165
+ y="${fontSize + 12}"
166
+ font-size="${fontSize}px"
167
+ font-family="${font}"
168
+ font-weight="bold"
169
+ fill="${color}"
170
+ text-anchor="end"
171
+ >
172
+ ${scaleSingle?.humanized ?? ''}
173
+ </text>
174
+ </svg>
175
+ `;
176
+ }
@@ -1,5 +1,5 @@
1
- import { AdornmentPosition } from '../types/jobObject';
2
- import { SkySpecification } from '@mapcreator/maplibre-gl';
1
+ import type { AdornmentPosition } from '@/types/jobObject';
2
+ import type { SkySpecification } from '@mapcreator/maplibre-gl';
3
3
  export declare const defaultSky: SkySpecification;
4
4
  type PositionConfig = {
5
5
  x: 'left' | 'right' | 'center';
@@ -0,0 +1,53 @@
1
+ export const defaultSky = {
2
+ 'sky-color': '#6babff',
3
+ 'sky-horizon-blend': 1,
4
+ 'horizon-color': [
5
+ 'interpolate-hcl',
6
+ ['linear'],
7
+ ['zoom'],
8
+ 0,
9
+ '#edf7fc',
10
+ 9,
11
+ '#edf7fc',
12
+ 10,
13
+ '#dcf0fa',
14
+ ],
15
+ 'horizon-fog-blend': 0.5,
16
+ 'fog-color': '#edf7fc',
17
+ 'fog-ground-blend': 0.5,
18
+ 'atmosphere-blend': 1,
19
+ };
20
+ export const positionConfig = {
21
+ top_left: {
22
+ x: 'left',
23
+ y: 'top',
24
+ },
25
+ top_center: {
26
+ x: 'center',
27
+ y: 'top',
28
+ },
29
+ top_right: {
30
+ x: 'right',
31
+ y: 'top',
32
+ },
33
+ right_center: {
34
+ x: 'right',
35
+ y: 'center',
36
+ },
37
+ bottom_right: {
38
+ x: 'right',
39
+ y: 'bottom',
40
+ },
41
+ bottom_center: {
42
+ x: 'center',
43
+ y: 'bottom',
44
+ },
45
+ bottom_left: {
46
+ x: 'left',
47
+ y: 'bottom',
48
+ },
49
+ left_center: {
50
+ x: 'left',
51
+ y: 'center',
52
+ },
53
+ };
@@ -0,0 +1,7 @@
1
+ export function useButtonControl(props) {
2
+ const btn = document.createElement('button');
3
+ btn.className = props.className;
4
+ btn.innerHTML = `${props.content}`;
5
+ btn.addEventListener('click', props.onClick);
6
+ return btn;
7
+ }
@@ -1,2 +1,2 @@
1
- import { Map } from '@mapcreator/maplibre-gl';
1
+ import type { Map } from '@mapcreator/maplibre-gl';
2
2
  export declare function useFullScreenButton(map: Map): HTMLElement;
@@ -0,0 +1,29 @@
1
+ import fullScreenIcon from '@/images/full-screen.svg?raw';
2
+ import { useButtonControl } from '@/controls/controls';
3
+ import { isFullscreenSupported, enterFullscreen, exitFullscreen, isFullscreen, } from '@/utils/fullscreen';
4
+ export function useFullScreenButton(map) {
5
+ const onClick = () => {
6
+ const mapContainer = map.getContainer();
7
+ if (isFullscreenSupported()) {
8
+ if (isFullscreen(mapContainer)) {
9
+ exitFullscreen();
10
+ }
11
+ else {
12
+ enterFullscreen(mapContainer);
13
+ }
14
+ }
15
+ else {
16
+ let url = document.location.href;
17
+ url += url.includes('?') ? '&' : '?';
18
+ url += 'close-tab';
19
+ window.open(url, '_blank');
20
+ }
21
+ };
22
+ const control = {
23
+ type: 'button',
24
+ className: 'control-button fullscreen-control-button',
25
+ content: fullScreenIcon,
26
+ onClick,
27
+ };
28
+ return useButtonControl(control);
29
+ }