diff --git a/client/public/map_pin_icon.svg b/client/public/map_pin_icon.svg new file mode 100644 index 0000000..fcacf90 --- /dev/null +++ b/client/public/map_pin_icon.svg @@ -0,0 +1,129 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/client/src/components/map/MapComponent.tsx b/client/src/components/map/MapComponent.tsx index d1c260c..1f69a0a 100644 --- a/client/src/components/map/MapComponent.tsx +++ b/client/src/components/map/MapComponent.tsx @@ -6,8 +6,8 @@ import Feature from 'ol/Feature' import { IRectCoords, SatelliteMapsProvider } from '../../interfaces/map' import { Extent, getCenter } from 'ol/extent' import { highlightStyleRed, highlightStyleYellow } from './MapStyles' -import { customMapSource, googleMapsSatelliteSource, yandexMapsSatelliteSource } from './MapSources' -import { Geometry } from 'ol/geom' +import { customMapSource, googleMapsSatelliteSource, regionsLayerSource, yandexMapsSatelliteSource } from './MapSources' +import { Geometry, Point } from 'ol/geom' import { fromExtent } from 'ol/geom/Polygon' import { Coordinate } from 'ol/coordinate' import { addInteractions, getFeatureByEntityId, handleImageDrop, loadFeatures, processFigure, processLine, zoomToFeature } from './mapUtils' @@ -39,7 +39,8 @@ import { useAppStore } from '../../store/app' import { getDistrictData, getRegionData, setDistrictsData, setRegionsData } from '../../store/regions' import { ArrowLeft24Regular } from '@fluentui/react-icons' import View from 'ol/View' -import { Style } from 'ol/style' +import { Fill, Icon, Stroke, Style } from 'ol/style' +import { fromLonLat } from 'ol/proj' const swrOptions: SWRConfiguration = { revalidateOnFocus: false @@ -76,7 +77,8 @@ const MapComponent = ({ drawingLayerSource, satLayer, staticMapLayer, figuresLayer, linesLayer, regionsLayer, districtBoundLayer, districtsLayer, - printAreaDraw, statusText, statusTextPosition, regionSelect, districtSelect + printAreaDraw, statusText, statusTextPosition, regionSelect, districtSelect, + capitalsLayer } = useMapStore().id[id] // Tab settings @@ -102,6 +104,11 @@ const MapComponent = ({ return res }), swrOptions) + const { data: regionsData } = useSWR(`/general/regions`, (url) => fetcher(url, BASE_URL.ems).then(res => { + setRegionsData(res) + return res + }), swrOptions) + // Bounds: region const { data: regionBoundsData } = useSWR(`/gis/bounds/region`, (url) => fetcher(url, BASE_URL.ems), swrOptions) @@ -140,6 +147,40 @@ const MapComponent = ({ } }, [regionBoundsData]) + useEffect(() => { + if (regionsData && Array.isArray(regionsData) && capitalsLayer) { + regionsData.map((region) => { + const lon = region.capital_longitude + const lat = region.capital_latitude + + if (lon != null && lat != null) { + const marker = new Feature({ + geometry: new Point(fromLonLat([lon, lat], 'EPSG:3857')), + regionId: region.id, // optional, useful for interactivity + }); + + marker.setStyle(new Style({ + image: new Icon({ + src: 'public/map_pin_icon.svg', + scale: 0.2 + }) + })) + + if (region.capital) { + marker.setProperties({ + entity_id: region.capital + }) + } + + // Optional styling for capital marker + + capitalsLayer.getSource()?.addFeature(marker); + } + + }) + } + }, [regionsData, capitalsLayer]) + useEffect(() => { if (selectedRegion === null && regionBoundsData) { if (map) { @@ -161,7 +202,6 @@ const MapComponent = ({ }) map.addInteraction(regionSelect) - } } }, [regionBoundsData, selectedRegion, map, regionsLayer]) @@ -323,11 +363,6 @@ const MapComponent = ({ } }, [currentObjectId, figuresLayer, linesLayer]) - const { data: regionsData } = useSWR(`/general/regions`, (url) => fetcher(url, BASE_URL.ems).then(res => { - setRegionsData(res) - return res - }), swrOptions) - const { data: districtsData } = useSWR(selectedRegion ? `/general/districts/?region_id=${selectedRegion}` : null, (url) => fetcher(url, BASE_URL.ems).then(res => { setDistrictsData(res) return res diff --git a/client/src/store/map.ts b/client/src/store/map.ts index 56d1e37..3802084 100644 --- a/client/src/store/map.ts +++ b/client/src/store/map.ts @@ -97,6 +97,7 @@ interface MapState { printScale: PrintScale; printScaleLine: boolean; selectionDragBox: DragBox; + capitalsLayer: VectorLayer }>; } @@ -189,6 +190,16 @@ export const initializeMapState = ( } }) + const capitalsLayer = new VectorLayer({ + source: new VectorSource(), + declutter: true, + properties: { + id: uuidv4(), + name: 'Capitals', + type: 'capital' + } + }) + const districtBoundLayer = new VectorImage({ style: new Style({ stroke: new Stroke({ color: 'red', width: 2 }), }) }) const districtsLayer = new VectorImage({ source: new VectorSource(), properties: { id: uuidv4(), name: 'Населенные пункты', type: 'district' } }) @@ -282,6 +293,7 @@ export const initializeMapState = ( districtsLayer, districtBoundLayer, regionsLayer, + capitalsLayer, figuresLayer, drawingLayer, imageLayer, @@ -327,6 +339,13 @@ export const initializeMapState = ( setStatusText(id, name); setStatusTextPosition(id, pixel[0], pixel[1]); } + + if (layer.get("type") === "capital" && feature.get("entity_id")) { + found = true + + setStatusText(id, feature.get("entity_id")) + setStatusTextPosition(id, pixel[0], pixel[1]); + } }); if (!found) { @@ -482,7 +501,8 @@ export const initializeMapState = ( printRes: 72, printScale: '250', printScaleLine: true, - selectionDragBox: selectionDragBox + selectionDragBox: selectionDragBox, + capitalsLayer: capitalsLayer } } }