diff options
-rw-r--r-- | modern/src/main/MainPage.js | 2 | ||||
-rw-r--r-- | modern/src/map/overlay/MapOverlay.js | 39 | ||||
-rw-r--r-- | modern/src/map/overlay/useMapOverlays.js | 29 | ||||
-rw-r--r-- | modern/src/settings/PreferencesPage.js | 27 | ||||
-rw-r--r-- | modern/src/settings/ServerPage.js | 5 | ||||
-rw-r--r-- | web/l10n/en.json | 3 |
6 files changed, 105 insertions, 0 deletions
diff --git a/modern/src/main/MainPage.js b/modern/src/main/MainPage.js index 4bc703f6..b93a3940 100644 --- a/modern/src/main/MainPage.js +++ b/modern/src/main/MainPage.js @@ -33,6 +33,7 @@ import MapLiveRoutes from '../map/main/MapLiveRoutes'; import { useDeviceReadonly } from '../common/util/permissions'; import MapPositions from '../map/MapPositions'; import MapDirection from '../map/MapDirection'; +import MapOverlay from '../map/overlay/MapOverlay'; const useStyles = makeStyles((theme) => ({ root: { @@ -199,6 +200,7 @@ const MainPage = () => { return ( <div className={classes.root}> <MapView> + <MapOverlay /> <MapGeofence /> <MapAccuracy /> {mapLiveRoutes && <MapLiveRoutes />} diff --git a/modern/src/map/overlay/MapOverlay.js b/modern/src/map/overlay/MapOverlay.js new file mode 100644 index 00000000..ec51c7c9 --- /dev/null +++ b/modern/src/map/overlay/MapOverlay.js @@ -0,0 +1,39 @@ +import { useEffect } from 'react'; +import usePersistedState from '../../common/util/usePersistedState'; +import { map } from '../core/MapView'; +import useMapOverlays from './useMapOverlays'; + +const MapOverlay = () => { + const id = 'overlay'; + + const mapOverlays = useMapOverlays(); + const [selectedMapOverlay] = usePersistedState('selectedMapOverlay'); + + const activeOverlay = mapOverlays.filter((overlay) => overlay.available).find((overlay) => overlay.id === selectedMapOverlay); + + useEffect(() => { + if (activeOverlay) { + map.addSource(id, activeOverlay.source); + map.addLayer({ + id, + type: 'raster', + source: id, + layout: { + visibility: 'visible', + }, + }); + } + return () => { + if (map.getLayer(id)) { + map.removeLayer(id); + } + if (map.getSource(id)) { + map.removeSource(id); + } + }; + }); + + return null; +}; + +export default MapOverlay; diff --git a/modern/src/map/overlay/useMapOverlays.js b/modern/src/map/overlay/useMapOverlays.js new file mode 100644 index 00000000..ede1e5b9 --- /dev/null +++ b/modern/src/map/overlay/useMapOverlays.js @@ -0,0 +1,29 @@ +import { useSelector } from 'react-redux'; +import { useTranslation } from '../../common/components/LocalizationProvider'; + +const sourceCustom = (url) => ({ + type: 'raster', + tiles: [url], + tileSize: 256, +}); + +export default () => { + const t = useTranslation(); + + const customMapOverlay = useSelector((state) => state.session.server?.overlayUrl); + + return [ + { + id: 'openSeaMap', + title: t('mapOpenSeaMap'), + source: sourceCustom('http://tiles.openseamap.org/seamark/{z}/{x}/{y}.png'), + available: true, + }, + { + id: 'custom', + title: t('mapOverlayCustom'), + source: sourceCustom(customMapOverlay), + available: !!customMapOverlay, + }, + ]; +}; diff --git a/modern/src/settings/PreferencesPage.js b/modern/src/settings/PreferencesPage.js index 2a2674f7..d6589231 100644 --- a/modern/src/settings/PreferencesPage.js +++ b/modern/src/settings/PreferencesPage.js @@ -14,6 +14,7 @@ import usePositionAttributes from '../common/attributes/usePositionAttributes'; import { prefixString, unprefixString } from '../common/util/stringUtils'; import SelectField from '../common/components/SelectField'; import useMapStyles from '../map/core/useMapStyles'; +import useMapOverlays from '../map/overlay/useMapOverlays'; const useStyles = makeStyles((theme) => ({ container: { @@ -40,6 +41,9 @@ const PreferencesPage = () => { const mapStyles = useMapStyles(); const [activeMapStyles, setActiveMapStyles] = usePersistedState('activeMapStyles', ['locationIqStreets', 'osm', 'carto']); + const mapOverlays = useMapOverlays(); + const [selectedMapOverlay, setSelectedMapOverlay] = usePersistedState('selectedMapOverlay'); + const positionAttributes = usePositionAttributes(t); const [positionItems, setPositionItems] = usePersistedState('positionItems', ['speed', 'address', 'totalDistance', 'course']); @@ -109,6 +113,29 @@ const PreferencesPage = () => { </Select> </FormControl> <FormControl> + <InputLabel>{t('mapOverlay')}</InputLabel> + <Select + label={t('mapOverlay')} + value={selectedMapOverlay} + onChange={(e) => { + const clicked = mapOverlays.find((o) => o.id === e.target.value); + if (!clicked || clicked.available) { + setSelectedMapOverlay(e.target.value); + } else if (clicked.id !== 'custom') { + const query = new URLSearchParams({ attribute: clicked.attribute }); + navigate(`/settings/user/${userId}?${query.toString()}`); + } + }} + > + <MenuItem value="">{'\u00a0'}</MenuItem> + {mapOverlays.map((overlay) => ( + <MenuItem key={overlay.id} value={overlay.id}> + <Typography component="span" color={overlay.available ? 'textPrimary' : 'error'}>{overlay.title}</Typography> + </MenuItem> + ))} + </Select> + </FormControl> + <FormControl> <InputLabel>{t('sharedAttributes')}</InputLabel> <Select label={t('sharedAttributes')} diff --git a/modern/src/settings/ServerPage.js b/modern/src/settings/ServerPage.js index 25b71e37..26ef5bbf 100644 --- a/modern/src/settings/ServerPage.js +++ b/modern/src/settings/ServerPage.js @@ -96,6 +96,11 @@ const ServerPage = () => { label={t('mapCustomLabel')} /> <TextField + value={item.overlayUrl || ''} + onChange={(event) => setItem({ ...item, overlayUrl: event.target.value })} + label={t('mapOverlayCustom')} + /> + <TextField type="number" value={item.latitude || 0} onChange={(event) => setItem({ ...item, latitude: Number(event.target.value) })} diff --git a/web/l10n/en.json b/web/l10n/en.json index ed37e0f7..0ee4dc3f 100644 --- a/web/l10n/en.json +++ b/web/l10n/en.json @@ -252,6 +252,9 @@ "serverAnnouncement": "Announcement", "mapTitle": "Map", "mapActive": "Active Maps", + "mapOverlay": "Map Overlay", + "mapOverlayCustom": "Custom Overlay", + "mapOpenSeaMap": "OpenSeaMap", "mapLayer": "Map Layer", "mapCustom": "Custom (XYZ)", "mapCustomArcgis": "Custom (ArcGIS)", |