aboutsummaryrefslogtreecommitdiff
path: root/modern/src/map/core
diff options
context:
space:
mode:
authorAnton Tananaev <anton@traccar.org>2024-04-06 09:22:10 -0700
committerAnton Tananaev <anton@traccar.org>2024-04-06 09:22:10 -0700
commitf418231b6b2f5e030a0d2dcc390c314602b1f740 (patch)
tree10326adf3792bc2697e06bb5f2b8ef2a8f7e55fe /modern/src/map/core
parentb392a4af78e01c8e0f50aad5468e9583675b24be (diff)
downloadtrackermap-web-f418231b6b2f5e030a0d2dcc390c314602b1f740.tar.gz
trackermap-web-f418231b6b2f5e030a0d2dcc390c314602b1f740.tar.bz2
trackermap-web-f418231b6b2f5e030a0d2dcc390c314602b1f740.zip
Move modern to the top
Diffstat (limited to 'modern/src/map/core')
-rw-r--r--modern/src/map/core/MapView.jsx123
-rw-r--r--modern/src/map/core/mapUtil.js105
-rw-r--r--modern/src/map/core/preloadImages.js78
-rw-r--r--modern/src/map/core/useMapStyles.js259
4 files changed, 0 insertions, 565 deletions
diff --git a/modern/src/map/core/MapView.jsx b/modern/src/map/core/MapView.jsx
deleted file mode 100644
index 35b3a65a..00000000
--- a/modern/src/map/core/MapView.jsx
+++ /dev/null
@@ -1,123 +0,0 @@
-import 'maplibre-gl/dist/maplibre-gl.css';
-import maplibregl from 'maplibre-gl';
-import React, {
- useRef, useLayoutEffect, useEffect, useState,
-} from 'react';
-import { SwitcherControl } from '../switcher/switcher';
-import { useAttributePreference, usePreference } from '../../common/util/preferences';
-import usePersistedState, { savePersistedState } from '../../common/util/usePersistedState';
-import { mapImages } from './preloadImages';
-import useMapStyles from './useMapStyles';
-
-const element = document.createElement('div');
-element.style.width = '100%';
-element.style.height = '100%';
-element.style.boxSizing = 'initial';
-
-export const map = new maplibregl.Map({
- container: element,
- attributionControl: false,
-});
-
-let ready = false;
-const readyListeners = new Set();
-
-const addReadyListener = (listener) => {
- readyListeners.add(listener);
- listener(ready);
-};
-
-const removeReadyListener = (listener) => {
- readyListeners.delete(listener);
-};
-
-const updateReadyValue = (value) => {
- ready = value;
- readyListeners.forEach((listener) => listener(value));
-};
-
-const initMap = async () => {
- if (ready) return;
- if (!map.hasImage('background')) {
- Object.entries(mapImages).forEach(([key, value]) => {
- map.addImage(key, value, {
- pixelRatio: window.devicePixelRatio,
- });
- });
- }
- updateReadyValue(true);
-};
-
-map.addControl(new maplibregl.NavigationControl());
-
-const switcher = new SwitcherControl(
- () => updateReadyValue(false),
- (styleId) => savePersistedState('selectedMapStyle', styleId),
- () => {
- map.once('styledata', () => {
- const waiting = () => {
- if (!map.loaded()) {
- setTimeout(waiting, 33);
- } else {
- initMap();
- }
- };
- waiting();
- });
- },
-);
-
-map.addControl(switcher);
-
-const MapView = ({ children }) => {
- const containerEl = useRef(null);
-
- const [mapReady, setMapReady] = useState(false);
-
- const mapStyles = useMapStyles();
- const activeMapStyles = useAttributePreference('activeMapStyles', 'locationIqStreets,osm,carto');
- const [defaultMapStyle] = usePersistedState('selectedMapStyle', usePreference('map', 'locationIqStreets'));
- const mapboxAccessToken = useAttributePreference('mapboxAccessToken');
- const maxZoom = useAttributePreference('web.maxZoom');
-
- useEffect(() => {
- if (maxZoom) {
- map.setMaxZoom(maxZoom);
- }
- }, [maxZoom]);
-
- useEffect(() => {
- maplibregl.accessToken = mapboxAccessToken;
- }, [mapboxAccessToken]);
-
- useEffect(() => {
- const filteredStyles = mapStyles.filter((s) => s.available && activeMapStyles.includes(s.id));
- const styles = filteredStyles.length ? filteredStyles : mapStyles.filter((s) => s.id === 'osm');
- switcher.updateStyles(styles, defaultMapStyle);
- }, [mapStyles, defaultMapStyle]);
-
- useEffect(() => {
- const listener = (ready) => setMapReady(ready);
- addReadyListener(listener);
- return () => {
- removeReadyListener(listener);
- };
- }, []);
-
- useLayoutEffect(() => {
- const currentEl = containerEl.current;
- currentEl.appendChild(element);
- map.resize();
- return () => {
- currentEl.removeChild(element);
- };
- }, [containerEl]);
-
- return (
- <div style={{ width: '100%', height: '100%' }} ref={containerEl}>
- {mapReady && children}
- </div>
- );
-};
-
-export default MapView;
diff --git a/modern/src/map/core/mapUtil.js b/modern/src/map/core/mapUtil.js
deleted file mode 100644
index 8dcded2c..00000000
--- a/modern/src/map/core/mapUtil.js
+++ /dev/null
@@ -1,105 +0,0 @@
-import { parse, stringify } from 'wellknown';
-import circle from '@turf/circle';
-
-export const loadImage = (url) => new Promise((imageLoaded) => {
- const image = new Image();
- image.onload = () => imageLoaded(image);
- image.src = url;
-});
-
-const canvasTintImage = (image, color) => {
- const canvas = document.createElement('canvas');
- canvas.width = image.width * devicePixelRatio;
- canvas.height = image.height * devicePixelRatio;
- canvas.style.width = `${image.width}px`;
- canvas.style.height = `${image.height}px`;
-
- const context = canvas.getContext('2d');
-
- context.save();
- context.fillStyle = color;
- context.globalAlpha = 1;
- context.fillRect(0, 0, canvas.width, canvas.height);
- context.globalCompositeOperation = 'destination-atop';
- context.globalAlpha = 1;
- context.drawImage(image, 0, 0, canvas.width, canvas.height);
- context.restore();
-
- return canvas;
-};
-
-export const prepareIcon = (background, icon, color) => {
- const canvas = document.createElement('canvas');
- canvas.width = background.width * devicePixelRatio;
- canvas.height = background.height * devicePixelRatio;
- canvas.style.width = `${background.width}px`;
- canvas.style.height = `${background.height}px`;
-
- const context = canvas.getContext('2d');
- context.drawImage(background, 0, 0, canvas.width, canvas.height);
-
- if (icon) {
- const iconRatio = 0.5;
- const imageWidth = canvas.width * iconRatio;
- const imageHeight = canvas.height * iconRatio;
- context.drawImage(canvasTintImage(icon, color), (canvas.width - imageWidth) / 2, (canvas.height - imageHeight) / 2, imageWidth, imageHeight);
- }
-
- return context.getImageData(0, 0, canvas.width, canvas.height);
-};
-
-export const reverseCoordinates = (it) => {
- if (!it) {
- return it;
- } if (Array.isArray(it)) {
- if (it.length === 2 && typeof it[0] === 'number' && typeof it[1] === 'number') {
- return [it[1], it[0]];
- }
- return it.map((it) => reverseCoordinates(it));
- }
- return {
- ...it,
- coordinates: reverseCoordinates(it.coordinates),
- };
-};
-
-export const geofenceToFeature = (theme, item) => {
- let geometry;
- if (item.area.indexOf('CIRCLE') > -1) {
- const coordinates = item.area.replace(/CIRCLE|\(|\)|,/g, ' ').trim().split(/ +/);
- const options = { steps: 32, units: 'meters' };
- const polygon = circle([Number(coordinates[1]), Number(coordinates[0])], Number(coordinates[2]), options);
- geometry = polygon.geometry;
- } else {
- geometry = reverseCoordinates(parse(item.area));
- }
- return {
- id: item.id,
- type: 'Feature',
- geometry,
- properties: {
- name: item.name,
- color: item.attributes.color || theme.palette.geometry.main,
- },
- };
-};
-
-export const geometryToArea = (geometry) => stringify(reverseCoordinates(geometry));
-
-export const findFonts = (map) => {
- const fontSet = new Set();
- const { layers } = map.getStyle();
- layers?.forEach?.((layer) => {
- layer.layout?.['text-font']?.forEach?.(fontSet.add, fontSet);
- });
- const availableFonts = [...fontSet];
- const regularFont = availableFonts.find((it) => it.includes('Regular'));
- if (regularFont) {
- return [regularFont];
- }
- const anyFont = availableFonts.find(Boolean);
- if (anyFont) {
- return [anyFont];
- }
- return ['Roboto Regular'];
-};
diff --git a/modern/src/map/core/preloadImages.js b/modern/src/map/core/preloadImages.js
deleted file mode 100644
index a0056d4c..00000000
--- a/modern/src/map/core/preloadImages.js
+++ /dev/null
@@ -1,78 +0,0 @@
-import { grey } from '@mui/material/colors';
-import createPalette from '@mui/material/styles/createPalette';
-import { loadImage, prepareIcon } from './mapUtil';
-
-import arrowSvg from '../../resources/images/arrow.svg';
-import directionSvg from '../../resources/images/direction.svg';
-import backgroundSvg from '../../resources/images/background.svg';
-import animalSvg from '../../resources/images/icon/animal.svg';
-import bicycleSvg from '../../resources/images/icon/bicycle.svg';
-import boatSvg from '../../resources/images/icon/boat.svg';
-import busSvg from '../../resources/images/icon/bus.svg';
-import carSvg from '../../resources/images/icon/car.svg';
-import camperSvg from '../../resources/images/icon/camper.svg';
-import craneSvg from '../../resources/images/icon/crane.svg';
-import defaultSvg from '../../resources/images/icon/default.svg';
-import helicopterSvg from '../../resources/images/icon/helicopter.svg';
-import motorcycleSvg from '../../resources/images/icon/motorcycle.svg';
-import offroadSvg from '../../resources/images/icon/offroad.svg';
-import personSvg from '../../resources/images/icon/person.svg';
-import pickupSvg from '../../resources/images/icon/pickup.svg';
-import planeSvg from '../../resources/images/icon/plane.svg';
-import scooterSvg from '../../resources/images/icon/scooter.svg';
-import shipSvg from '../../resources/images/icon/ship.svg';
-import tractorSvg from '../../resources/images/icon/tractor.svg';
-import trainSvg from '../../resources/images/icon/train.svg';
-import tramSvg from '../../resources/images/icon/tram.svg';
-import trolleybusSvg from '../../resources/images/icon/trolleybus.svg';
-import truckSvg from '../../resources/images/icon/truck.svg';
-import vanSvg from '../../resources/images/icon/van.svg';
-
-export const mapIcons = {
- animal: animalSvg,
- bicycle: bicycleSvg,
- boat: boatSvg,
- bus: busSvg,
- car: carSvg,
- camper: camperSvg,
- crane: craneSvg,
- default: defaultSvg,
- helicopter: helicopterSvg,
- motorcycle: motorcycleSvg,
- offroad: offroadSvg,
- person: personSvg,
- pickup: pickupSvg,
- plane: planeSvg,
- scooter: scooterSvg,
- ship: shipSvg,
- tractor: tractorSvg,
- train: trainSvg,
- tram: tramSvg,
- trolleybus: trolleybusSvg,
- truck: truckSvg,
- van: vanSvg,
-};
-
-export const mapIconKey = (category) => (mapIcons.hasOwnProperty(category) ? category : 'default');
-
-export const mapImages = {};
-
-const mapPalette = createPalette({
- neutral: { main: grey[500] },
-});
-
-export default async () => {
- const background = await loadImage(backgroundSvg);
- mapImages.background = await prepareIcon(background);
- mapImages.direction = await prepareIcon(await loadImage(directionSvg));
- mapImages.arrow = await prepareIcon(await loadImage(arrowSvg));
- await Promise.all(Object.keys(mapIcons).map(async (category) => {
- const results = [];
- ['info', 'success', 'error', 'neutral'].forEach((color) => {
- results.push(loadImage(mapIcons[category]).then((icon) => {
- mapImages[`${category}-${color}`] = prepareIcon(background, icon, mapPalette[color].main);
- }));
- });
- await Promise.all(results);
- }));
-};
diff --git a/modern/src/map/core/useMapStyles.js b/modern/src/map/core/useMapStyles.js
deleted file mode 100644
index 7c3412b5..00000000
--- a/modern/src/map/core/useMapStyles.js
+++ /dev/null
@@ -1,259 +0,0 @@
-import { useMemo } from 'react';
-import { useSelector } from 'react-redux';
-import { useTranslation } from '../../common/components/LocalizationProvider';
-import { useAttributePreference } from '../../common/util/preferences';
-
-const styleCustom = ({ tiles, minZoom, maxZoom, attribution }) => {
- const source = {
- type: 'raster',
- tiles,
- attribution,
- tileSize: 256,
- minzoom: minZoom,
- maxzoom: maxZoom,
- };
- Object.keys(source).forEach((key) => source[key] === undefined && delete source[key]);
- return {
- version: 8,
- sources: {
- custom: source,
- },
- glyphs: 'https://cdn.traccar.com/map/fonts/{fontstack}/{range}.pbf',
- layers: [{
- id: 'custom',
- type: 'raster',
- source: 'custom',
- }],
- };
-};
-
-export default () => {
- const t = useTranslation();
-
- const mapTilerKey = useAttributePreference('mapTilerKey');
- const locationIqKey = useAttributePreference('locationIqKey') || 'pk.0f147952a41c555a5b70614039fd148b';
- const bingMapsKey = useAttributePreference('bingMapsKey');
- const tomTomKey = useAttributePreference('tomTomKey');
- const hereKey = useAttributePreference('hereKey');
- const mapboxAccessToken = useAttributePreference('mapboxAccessToken');
- const customMapUrl = useSelector((state) => state.session.server.mapUrl);
-
- return useMemo(() => [
- {
- id: 'locationIqStreets',
- title: t('mapLocationIqStreets'),
- style: `https://tiles.locationiq.com/v3/streets/vector.json?key=${locationIqKey}`,
- available: true,
- },
- {
- id: 'locationIqDark',
- title: t('mapLocationIqDark'),
- style: `https://tiles.locationiq.com/v3/dark/vector.json?key=${locationIqKey}`,
- available: true,
- },
- {
- id: 'osm',
- title: t('mapOsm'),
- style: styleCustom({
- tiles: ['https://tile.openstreetmap.org/{z}/{x}/{y}.png'],
- maxZoom: 19,
- attribution: '© <a target="_top" rel="noopener" href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
- }),
- available: true,
- },
- {
- id: 'openTopoMap',
- title: t('mapOpenTopoMap'),
- style: styleCustom({
- tiles: ['a', 'b', 'c'].map((i) => `https://${i}.tile.opentopomap.org/{z}/{x}/{y}.png`),
- maxZoom: 17,
- }),
- available: true,
- },
- {
- id: 'carto',
- title: t('mapCarto'),
- style: styleCustom({
- tiles: ['a', 'b', 'c', 'd'].map((i) => `https://${i}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}@2x.png`),
- maxZoom: 22,
- attribution: '© <a target="_top" rel="noopener" href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, © <a target="_top" rel="noopener" href="https://carto.com/attribution">CARTO</a>',
- }),
- available: true,
- },
- {
- id: 'googleRoad',
- title: t('mapGoogleRoad'),
- style: styleCustom({
- tiles: [0, 1, 2, 3].map((i) => `https://mt${i}.google.com/vt/lyrs=m&hl=en&x={x}&y={y}&z={z}&s=Ga`),
- maxZoom: 20,
- attribution: '© Google',
- }),
- available: true,
- },
- {
- id: 'googleSatellite',
- title: t('mapGoogleSatellite'),
- style: styleCustom({
- tiles: [0, 1, 2, 3].map((i) => `https://mt${i}.google.com/vt/lyrs=s&hl=en&x={x}&y={y}&z={z}&s=Ga`),
- maxZoom: 20,
- attribution: '© Google',
- }),
- available: true,
- },
- {
- id: 'googleHybrid',
- title: t('mapGoogleHybrid'),
- style: styleCustom({
- tiles: [0, 1, 2, 3].map((i) => `https://mt${i}.google.com/vt/lyrs=y&hl=en&x={x}&y={y}&z={z}&s=Ga`),
- maxZoom: 20,
- attribution: '© Google',
- }),
- available: true,
- },
- {
- id: 'mapTilerBasic',
- title: t('mapMapTilerBasic'),
- style: `https://api.maptiler.com/maps/basic/style.json?key=${mapTilerKey}`,
- available: !!mapTilerKey,
- attribute: 'mapTilerKey',
- },
- {
- id: 'mapTilerHybrid',
- title: t('mapMapTilerHybrid'),
- style: `https://api.maptiler.com/maps/hybrid/style.json?key=${mapTilerKey}`,
- available: !!mapTilerKey,
- attribute: 'mapTilerKey',
- },
- {
- id: 'bingRoad',
- title: t('mapBingRoad'),
- style: styleCustom({
- tiles: [0, 1, 2, 3].map((i) => `https://t${i}.ssl.ak.dynamic.tiles.virtualearth.net/comp/ch/{quadkey}?mkt=en-US&it=G,L&shading=hill&og=1885&n=z`),
- maxZoom: 21,
- }),
- available: !!bingMapsKey,
- attribute: 'bingMapsKey',
- },
- {
- id: 'bingAerial',
- title: t('mapBingAerial'),
- style: styleCustom({
- tiles: [0, 1, 2, 3].map((i) => `https://ecn.t${i}.tiles.virtualearth.net/tiles/a{quadkey}.jpeg?g=12327`),
- maxZoom: 19,
- }),
- available: !!bingMapsKey,
- attribute: 'bingMapsKey',
- },
- {
- id: 'bingHybrid',
- title: t('mapBingHybrid'),
- style: styleCustom({
- tiles: [0, 1, 2, 3].map((i) => `https://t${i}.ssl.ak.dynamic.tiles.virtualearth.net/comp/ch/{quadkey}?mkt=en-US&it=A,G,L&og=1885&n=z`),
- maxZoom: 19,
- }),
- available: !!bingMapsKey,
- attribute: 'bingMapsKey',
- },
- {
- id: 'tomTomBasic',
- title: t('mapTomTomBasic'),
- style: `https://api.tomtom.com/map/1/style/20.0.0-8/basic_main.json?key=${tomTomKey}`,
- available: !!tomTomKey,
- attribute: 'tomTomKey',
- },
- {
- id: 'hereBasic',
- title: t('mapHereBasic'),
- style: `https://assets.vector.hereapi.com/styles/berlin/base/mapbox/tilezen?apikey=${hereKey}`,
- available: !!hereKey,
- attribute: 'hereKey',
- },
- {
- id: 'hereHybrid',
- title: t('mapHereHybrid'),
- style: styleCustom({
- tiles: [1, 2, 3, 4].map((i) => `https://${i}.aerial.maps.ls.hereapi.com/maptile/2.1/maptile/newest/hybrid.day/{z}/{x}/{y}/256/png8?apiKey=${hereKey}`),
- maxZoom: 20,
- }),
- available: !!hereKey,
- attribute: 'hereKey',
- },
- {
- id: 'hereSatellite',
- title: t('mapHereSatellite'),
- style: styleCustom({
- tiles: [1, 2, 3, 4].map((i) => `https://${i}.aerial.maps.ls.hereapi.com/maptile/2.1/maptile/newest/satellite.day/{z}/{x}/{y}/256/png8?apiKey=${hereKey}`),
- maxZoom: 19,
- }),
- available: !!hereKey,
- attribute: 'hereKey',
- },
- {
- id: 'autoNavi',
- title: t('mapAutoNavi'),
- style: styleCustom({
- tiles: [1, 2, 3, 4].map((i) => `https://webrd0${i}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}`),
- minZoom: 3,
- maxZoom: 18,
- }),
- available: true,
- },
- {
- id: 'ordnanceSurvey',
- title: t('mapOrdnanceSurvey'),
- style: 'https://api.os.uk/maps/vector/v1/vts/resources/styles?key=EAZ8p83u72FTGiLjLC2MsTAl1ko6XQHC',
- transformRequest: (url) => ({
- url: `${url}&srs=3857`,
- }),
- available: true,
- },
- {
- id: 'mapboxStreets',
- title: t('mapMapboxStreets'),
- style: styleCustom({
- tiles: [`https://api.mapbox.com/styles/v1/mapbox/streets-v11/tiles/{z}/{x}/{y}?access_token=${mapboxAccessToken}`],
- maxZoom: 22,
- }),
- available: !!mapboxAccessToken,
- attribute: 'mapboxAccessToken',
- },
- {
- id: 'mapboxStreetsDark',
- title: t('mapMapboxStreetsDark'),
- style: styleCustom({
- tiles: [`https://api.mapbox.com/styles/v1/mapbox/dark-v11/tiles/{z}/{x}/{y}?access_token=${mapboxAccessToken}`],
- maxZoom: 22,
- }),
- available: !!mapboxAccessToken,
- attribute: 'mapboxAccessToken',
- },
- {
- id: 'mapboxOutdoors',
- title: t('mapMapboxOutdoors'),
- style: styleCustom({
- tiles: [`https://api.mapbox.com/styles/v1/mapbox/outdoors-v11/tiles/{z}/{x}/{y}?access_token=${mapboxAccessToken}`],
- maxZoom: 22,
- }),
- available: !!mapboxAccessToken,
- attribute: 'mapboxAccessToken',
- },
- {
- id: 'mapboxSatelliteStreet',
- title: t('mapMapboxSatellite'),
- style: styleCustom({
- tiles: [`https://api.mapbox.com/styles/v1/mapbox/satellite-streets-v11/tiles/{z}/{x}/{y}?access_token=${mapboxAccessToken}`],
- maxZoom: 22,
- }),
- available: !!mapboxAccessToken,
- attribute: 'mapboxAccessToken',
- },
- {
- id: 'custom',
- title: t('mapCustom'),
- style: styleCustom({
- tiles: [customMapUrl],
- }),
- available: !!customMapUrl,
- },
- ], [t, mapTilerKey, locationIqKey, bingMapsKey, tomTomKey, hereKey, mapboxAccessToken, customMapUrl]);
-};