@map-colonies/react-components 3.11.0 → 3.12.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 (32) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/cesium-map/layers/imagery.layer.js +9 -5
  3. package/dist/cesium-map/layers-manager.d.ts +10 -2
  4. package/dist/cesium-map/layers-manager.js +19 -1
  5. package/dist/cesium-map/map-legend/MapLegend.css +135 -0
  6. package/dist/cesium-map/map-legend/MapLegend.d.ts +15 -0
  7. package/dist/cesium-map/map-legend/MapLegend.js +57 -0
  8. package/dist/cesium-map/map-legend/MapLegendList.d.ts +13 -0
  9. package/dist/cesium-map/map-legend/MapLegendList.js +43 -0
  10. package/dist/cesium-map/map-legend/MapLegendSidebar.d.ts +16 -0
  11. package/dist/cesium-map/map-legend/MapLegendSidebar.js +20 -0
  12. package/dist/cesium-map/map-legend/MapLegendToggle.d.ts +7 -0
  13. package/dist/cesium-map/map-legend/MapLegendToggle.js +20 -0
  14. package/dist/cesium-map/map-legend/index.d.ts +3 -0
  15. package/dist/cesium-map/map-legend/index.js +14 -0
  16. package/dist/cesium-map/map.css +6 -1
  17. package/dist/cesium-map/map.d.ts +16 -1
  18. package/dist/cesium-map/map.js +51 -21
  19. package/dist/cesium-map/settings/settings.css +5 -2
  20. package/package.json +3 -3
  21. package/src/lib/cesium-map/layers/imagery.layer.tsx +12 -7
  22. package/src/lib/cesium-map/layers-manager.ts +35 -2
  23. package/src/lib/cesium-map/map-legend/MapLegend.css +135 -0
  24. package/src/lib/cesium-map/map-legend/MapLegend.tsx +91 -0
  25. package/src/lib/cesium-map/map-legend/MapLegendList.tsx +46 -0
  26. package/src/lib/cesium-map/map-legend/MapLegendSidebar.tsx +55 -0
  27. package/src/lib/cesium-map/map-legend/MapLegendToggle.tsx +31 -0
  28. package/src/lib/cesium-map/map-legend/index.tsx +3 -0
  29. package/src/lib/cesium-map/map-legend/legends-sidebar.stories.tsx +201 -0
  30. package/src/lib/cesium-map/map.css +6 -1
  31. package/src/lib/cesium-map/map.tsx +86 -20
  32. package/src/lib/cesium-map/settings/settings.css +5 -2
@@ -0,0 +1,201 @@
1
+ import React, { useState } from 'react';
2
+ import { Story, Meta } from '@storybook/react/types-6-0';
3
+ import { CesiumMap } from '../map';
4
+ import { CesiumXYZLayer } from '../layers/xyz.layer';
5
+ import { CesiumSceneMode } from '../map.types';
6
+
7
+ export default {
8
+ title: 'Cesium Map',
9
+ component: CesiumMap,
10
+ parameters: {
11
+ layout: 'fullscreen',
12
+ },
13
+ } as Meta;
14
+
15
+ const mapDivStyle = {
16
+ height: '100%',
17
+ width: '100%',
18
+ position: 'absolute' as const,
19
+ };
20
+
21
+ const optionsXYZSanDiego = {
22
+ url:
23
+ 'https://tiles.openaerialmap.org/5d73614588556200055f10d6/0/5d73614588556200055f10d7/{z}/{x}/{y}',
24
+ };
25
+
26
+ const BASE_MAPS = {
27
+ maps: [
28
+ {
29
+ id: '1st',
30
+ title: '1st Map Title',
31
+ isCurrent: true,
32
+ thumbnail:
33
+ 'https://nsw.digitaltwin.terria.io/build/3456d1802ab2ef330ae2732387726771.png',
34
+ baseRasteLayers: [
35
+ {
36
+ id: 'GOOGLE_TERRAIN',
37
+ type: 'XYZ_LAYER',
38
+ opacity: 1,
39
+ zIndex: 0,
40
+ options: {
41
+ url: 'https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}',
42
+ layers: '',
43
+ credit: 'GOOGLE',
44
+ },
45
+ },
46
+ {
47
+ id: 'INFRARED_RASTER',
48
+ type: 'WMS_LAYER',
49
+ opacity: 0.6,
50
+ zIndex: 1,
51
+ options: {
52
+ url:
53
+ 'https://mesonet.agron.iastate.edu/cgi-bin/wms/goes/conus_ir.cgi?',
54
+ layers: 'goes_conus_ir',
55
+ credit: 'Infrared data courtesy Iowa Environmental Mesonet',
56
+ parameters: {
57
+ transparent: 'true',
58
+ format: 'image/png',
59
+ },
60
+ },
61
+ },
62
+ ],
63
+ baseVectorLayers: [],
64
+ },
65
+ {
66
+ id: '2nd',
67
+ title: '2nd Map Title',
68
+ thumbnail:
69
+ 'https://nsw.digitaltwin.terria.io/build/efa2f6c408eb790753a9b5fb2f3dc678.png',
70
+ baseRasteLayers: [
71
+ {
72
+ id: 'RADAR_RASTER',
73
+ type: 'WMS_LAYER',
74
+ opacity: 0.6,
75
+ zIndex: 1,
76
+ options: {
77
+ url:
78
+ 'https://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi?',
79
+ layers: 'nexrad-n0r',
80
+ credit: 'Radar data courtesy Iowa Environmental Mesonet',
81
+ parameters: {
82
+ transparent: 'true',
83
+ format: 'image/png',
84
+ },
85
+ },
86
+ },
87
+ {
88
+ id: 'GOOGLE_TERRAIN',
89
+ type: 'XYZ_LAYER',
90
+ opacity: 1,
91
+ zIndex: 0,
92
+ options: {
93
+ url: 'https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}',
94
+ layers: '',
95
+ credit: 'GOOGLE',
96
+ },
97
+ },
98
+ {
99
+ id: 'VECTOR_TILES_GPS',
100
+ type: 'XYZ_LAYER',
101
+ opacity: 1,
102
+ zIndex: 2,
103
+ options: {
104
+ url: 'https://gps.tile.openstreetmap.org/lines/{z}/{x}/{y}.png',
105
+ layers: '',
106
+ credit: 'openstreetmap',
107
+ },
108
+ },
109
+ ],
110
+ baseVectorLayers: [],
111
+ },
112
+ {
113
+ id: '3rd',
114
+ title: '3rd Map Title',
115
+ thumbnail:
116
+ 'https://nsw.digitaltwin.terria.io/build/d8b97d3e38a0d43e5a06dea9aae17a3e.png',
117
+ baseRasteLayers: [
118
+ {
119
+ id: 'VECTOR_TILES',
120
+ type: 'XYZ_LAYER',
121
+ opacity: 1,
122
+ zIndex: 0,
123
+ options: {
124
+ url:
125
+ 'https://{s}.tile.thunderforest.com/cycle/{z}/{x}/{y}.png?apikey=6170aad10dfd42a38d4d8c709a536f38',
126
+ layers: '',
127
+ credit: 'thunderforest',
128
+ },
129
+ },
130
+ {
131
+ id: 'VECTOR_TILES_GPS',
132
+ type: 'XYZ_LAYER',
133
+ opacity: 1,
134
+ zIndex: 1,
135
+ options: {
136
+ url: 'https://gps.tile.openstreetmap.org/lines/{z}/{x}/{y}.png',
137
+ layers: '',
138
+ credit: 'openstreetmap',
139
+ },
140
+ },
141
+ {
142
+ id: 'WMTS_POPULATION_TILES',
143
+ type: 'WMTS_LAYER',
144
+ opacity: 0.4,
145
+ zIndex: 2,
146
+ options: {
147
+ url:
148
+ 'https://services.arcgisonline.com/arcgis/rest/services/Demographics/USA_Population_Density/MapServer/WMTS/',
149
+ layer: 'USGSShadedReliefOnly',
150
+ style: 'default',
151
+ format: 'image/jpeg',
152
+ tileMatrixSetID: 'default028mm',
153
+ maximumLevel: 19,
154
+ credit: 'U. S. Geological Survey',
155
+ },
156
+ },
157
+ ],
158
+ baseVectorLayers: [],
159
+ },
160
+ ],
161
+ };
162
+
163
+ export const MapWithLegends: Story = () => {
164
+ const [center] = useState<[number, number]>([
165
+ -117.30644008676421,
166
+ 33.117098433617564,
167
+ ]); //Sandiego Poinsettia Park
168
+ return (
169
+ <div style={mapDivStyle}>
170
+ <CesiumMap
171
+ center={center}
172
+ zoom={14}
173
+ imageryProvider={false}
174
+ sceneModes={[CesiumSceneMode.SCENE3D, CesiumSceneMode.COLUMBUS_VIEW]}
175
+ // @ts-ignore
176
+ baseMaps={BASE_MAPS}
177
+ legends={{
178
+ legendsList: [
179
+ {
180
+ layer: 'bluemarble',
181
+ legendImg:
182
+ 'https://c8.alamy.com/comp/F5HF5D/map-icon-legend-symbol-sign-toolkit-element-F5HF5D.jpg',
183
+ legendDoc: 'http://www.africau.edu/images/default/sample.pdf',
184
+ },
185
+ {
186
+ layer: 'bluemarble2',
187
+ legendImg:
188
+ 'https://i.pinimg.com/564x/55/cf/a1/55cfa147dfef99d231ec95ab8cd3652d--outdoor-code-cub-scouts-brownie-hiking-badge.jpg',
189
+ legendDoc: 'http://www.africau.edu/images/default/sample.pdf',
190
+ },
191
+ ],
192
+ title: 'Map Legends',
193
+ emptyText: 'No legends for this basemap',
194
+ }}
195
+ >
196
+ <CesiumXYZLayer options={optionsXYZSanDiego} />
197
+ </CesiumMap>
198
+ </div>
199
+ );
200
+ };
201
+ MapWithLegends.storyName = 'Map with legends sidebar';
@@ -40,7 +40,7 @@ body[dir='rtl'] .toolsContainer {
40
40
 
41
41
  .sideToolsContainer {
42
42
  display: flex;
43
- flex-direction: column;
43
+ flex-direction: row;
44
44
  gap: 8px;
45
45
  z-index: 2000;
46
46
  position: absolute;
@@ -52,3 +52,8 @@ body[dir='rtl'] .sideToolsContainer {
52
52
  left: 40px;
53
53
  right: unset;
54
54
  }
55
+
56
+ .viewer {
57
+ display: flex;
58
+ flex-direction: row;
59
+ }
@@ -4,7 +4,9 @@ import React, {
4
4
  useEffect,
5
5
  useState,
6
6
  useRef,
7
+ useCallback,
7
8
  } from 'react';
9
+ import { createPortal } from 'react-dom';
8
10
  import { Viewer, CesiumComponentRef } from 'resium';
9
11
  import { ViewerProps } from 'resium/dist/types/src/Viewer/Viewer';
10
12
  import {
@@ -26,7 +28,8 @@ import { Proj } from '../utils/projections';
26
28
  import { CoordinatesTrackerTool } from './tools/coordinates-tracker.tool';
27
29
  import { ScaleTrackerTool } from './tools/scale-tracker.tool';
28
30
  import { CesiumSettings, IBaseMap, IBaseMaps } from './settings/settings';
29
- import LayerManager from './layers-manager';
31
+ import { IMapLegend, MapLegendSidebar, MapLegendToggle } from './map-legend';
32
+ import LayerManager, { LegendExtractor } from './layers-manager';
30
33
  import { CesiumSceneMode, CesiumSceneModeEnum } from './map.types';
31
34
 
32
35
  import './map.css';
@@ -80,6 +83,14 @@ export interface IContextMenuData {
80
83
  handleClose: () => void;
81
84
  }
82
85
 
86
+ interface ILegends {
87
+ legendsList?: IMapLegend[];
88
+ emptyText?: string;
89
+ title?: string;
90
+ actionsTexts?: { docText: string; imgText: string };
91
+ mapLegendsExtractor?: LegendExtractor;
92
+ }
93
+
83
94
  export interface CesiumMapProps extends ViewerProps {
84
95
  showMousePosition?: boolean;
85
96
  showScale?: boolean;
@@ -96,6 +107,9 @@ export interface CesiumMapProps extends ViewerProps {
96
107
  width: number;
97
108
  dynamicHeightIncrement?: number;
98
109
  };
110
+ legendSidebarTitle?: string;
111
+ noLegendsText?: string;
112
+ legends?: ILegends;
99
113
  }
100
114
 
101
115
  export const useCesiumMap = (): CesiumViewer => {
@@ -119,11 +133,15 @@ export const CesiumMap: React.FC<CesiumMapProps> = (props) => {
119
133
  const [sceneModes, setSceneModes] = useState<
120
134
  CesiumSceneModeEnum[] | undefined
121
135
  >();
136
+ const [legendsList, setLegendsList] = useState<IMapLegend[]>([]);
122
137
  const [baseMaps, setBaseMaps] = useState<IBaseMaps | undefined>();
123
138
  const [showImageryMenu, setShowImageryMenu] = useState<boolean>(false);
124
139
  const [imageryMenuPosition, setImageryMenuPosition] = useState<
125
140
  Record<string, unknown> | undefined
126
141
  >(undefined);
142
+ const [isLegendsSidebarOpen, setIsLegendsSidebarOpen] = useState<boolean>(
143
+ false
144
+ );
127
145
 
128
146
  const viewerProps = {
129
147
  fullscreenButton: true,
@@ -163,7 +181,7 @@ export const CesiumMap: React.FC<CesiumMapProps> = (props) => {
163
181
  useEffect(() => {
164
182
  if (ref.current) {
165
183
  const viewer = ref.current.cesiumElement as CesiumViewer;
166
- viewer.layersManager = new LayerManager(viewer);
184
+
167
185
  if (props.imageryContextMenu) {
168
186
  viewer.screenSpaceEventHandler.setInputAction(
169
187
  (evt: Record<string, unknown>) => {
@@ -179,6 +197,20 @@ export const CesiumMap: React.FC<CesiumMapProps> = (props) => {
179
197
  setMapViewRef(ref.current?.cesiumElement);
180
198
  }, [ref, props.imageryContextMenu]);
181
199
 
200
+ useEffect(() => {
201
+ if (mapViewRef) {
202
+ mapViewRef.layersManager = new LayerManager(
203
+ mapViewRef,
204
+ props.legends?.mapLegendsExtractor,
205
+ () => {
206
+ setLegendsList(
207
+ mapViewRef?.layersManager?.legendsList as IMapLegend[]
208
+ );
209
+ }
210
+ );
211
+ }
212
+ }, [mapViewRef]);
213
+
182
214
  useEffect(() => {
183
215
  setSceneModes(
184
216
  props.sceneModes ?? [
@@ -326,27 +358,61 @@ export const CesiumMap: React.FC<CesiumMapProps> = (props) => {
326
358
  }
327
359
  }, [props.zoom, props.center, mapViewRef]);
328
360
 
361
+ const bindCustomToolsToViewer = useCallback((): JSX.Element | undefined => {
362
+ return (
363
+ mapViewRef &&
364
+ createPortal(
365
+ <>
366
+ <Box className="sideToolsContainer">
367
+ <CesiumSettings
368
+ sceneModes={sceneModes as CesiumSceneModeEnum[]}
369
+ baseMaps={baseMaps}
370
+ locale={locale}
371
+ />
372
+ <MapLegendToggle
373
+ onClick={() => setIsLegendsSidebarOpen(!isLegendsSidebarOpen)}
374
+ />
375
+ </Box>
376
+ <Box className="toolsContainer">
377
+ {showMousePosition === true ? (
378
+ <CoordinatesTrackerTool
379
+ projection={projection}
380
+ ></CoordinatesTrackerTool>
381
+ ) : (
382
+ <></>
383
+ )}
384
+ {showScale === true ? <ScaleTrackerTool locale={locale} /> : <></>}
385
+ </Box>
386
+ </>,
387
+ document.querySelector('.cesium-viewer') as Element
388
+ )
389
+ );
390
+ }, [
391
+ baseMaps,
392
+ locale,
393
+ mapViewRef,
394
+ projection,
395
+ sceneModes,
396
+ showMousePosition,
397
+ showScale,
398
+ isLegendsSidebarOpen,
399
+ ]);
400
+
329
401
  return (
330
- <Viewer full ref={ref} {...viewerProps}>
402
+ <Viewer className="viewer" full ref={ref} {...viewerProps}>
331
403
  <MapViewProvider value={mapViewRef as CesiumViewer}>
404
+ <MapLegendSidebar
405
+ title={props.legends?.title}
406
+ isOpen={isLegendsSidebarOpen}
407
+ toggleSidebar={(): void =>
408
+ setIsLegendsSidebarOpen(!isLegendsSidebarOpen)
409
+ }
410
+ noLegendsText={props.legends?.emptyText}
411
+ legends={props.legends?.legendsList ?? legendsList}
412
+ actionsTexts={props.legends?.actionsTexts}
413
+ />
332
414
  {props.children}
333
- <Box className="sideToolsContainer">
334
- <CesiumSettings
335
- sceneModes={sceneModes as CesiumSceneModeEnum[]}
336
- baseMaps={baseMaps}
337
- locale={locale}
338
- />
339
- </Box>
340
- <Box className="toolsContainer">
341
- {showMousePosition === true ? (
342
- <CoordinatesTrackerTool
343
- projection={projection}
344
- ></CoordinatesTrackerTool>
345
- ) : (
346
- <></>
347
- )}
348
- {showScale === true ? <ScaleTrackerTool locale={locale} /> : <></>}
349
- </Box>
415
+ {bindCustomToolsToViewer()}
350
416
  {props.imageryContextMenu &&
351
417
  showImageryMenu &&
352
418
  imageryMenuPosition &&
@@ -3,7 +3,8 @@
3
3
  gap: 8px;
4
4
  }
5
5
 
6
- .settingsIconContainer {
6
+ .settingsIconContainer,
7
+ .mapLegendToggleContainer {
7
8
  fill: #fff;
8
9
  width: 40px;
9
10
  height: 40px;
@@ -12,7 +13,8 @@
12
13
  border-radius: 4px;
13
14
  }
14
15
 
15
- .settingsIconContainer:hover {
16
+ .settingsIconContainer:hover,
17
+ .mapLegendToggleContainer:hover {
16
18
  background: #48b;
17
19
  border-color: #aef;
18
20
  }
@@ -35,6 +37,7 @@
35
37
  .settingsDialogPortal .mdc-dialog__container {
36
38
  align-items: unset;
37
39
  }
40
+
38
41
  .settingsDialogPortal .mdc-dialog .mdc-dialog__surface {
39
42
  max-height: unset;
40
43
  }