forked from VinokurovVE/tests
Map caching, clickhouse test service
This commit is contained in:
@ -4,32 +4,20 @@ import 'ol/ol.css'
|
||||
import Map from 'ol/Map'
|
||||
import View from 'ol/View'
|
||||
import { Draw, Modify, Select, Snap, Translate } from 'ol/interaction'
|
||||
import { OSM, Source, Vector as VectorSource, XYZ } from 'ol/source'
|
||||
import { OSM, Vector as VectorSource, XYZ } from 'ol/source'
|
||||
import { Tile as TileLayer, Vector as VectorLayer } from 'ol/layer'
|
||||
import { get, Projection, transform, transformExtent } from 'ol/proj'
|
||||
import { Divider, IconButton, Slider, Stack, Select as MUISelect, MenuItem, Container, Box } from '@mui/material'
|
||||
import { Adjust, Api, CircleOutlined, DoubleArrow, FeaturedVideoSharp, Handyman, OpenWith, RectangleOutlined, Timeline, Undo, Warning } from '@mui/icons-material'
|
||||
import { Divider, IconButton, Slider, Stack, Select as MUISelect, MenuItem, Box } from '@mui/material'
|
||||
import { Adjust, Api, CircleOutlined, OpenWith, RectangleOutlined, Rule, Straighten, Timeline, Undo, Warning } from '@mui/icons-material'
|
||||
import { Type } from 'ol/geom/Geometry'
|
||||
import { altKeyOnly, click, doubleClick, noModifierKeys, platformModifierKey, pointerMove, shiftKeyOnly, singleClick } from 'ol/events/condition'
|
||||
import Feature, { FeatureLike } from 'ol/Feature'
|
||||
import Style from 'ol/style/Style'
|
||||
import Fill from 'ol/style/Fill'
|
||||
import Stroke from 'ol/style/Stroke'
|
||||
import { FlatStyleLike } from 'ol/style/flat'
|
||||
import { click, noModifierKeys, shiftKeyOnly } from 'ol/events/condition'
|
||||
import Feature from 'ol/Feature'
|
||||
import { SatelliteMapsProvider } from '../../interfaces/map'
|
||||
import Tile from 'ol/Tile'
|
||||
import ImageTile from 'ol/ImageTile'
|
||||
import { createXYZ, TileGrid } from 'ol/tilegrid'
|
||||
import { TileCoord } from 'ol/tilecoord'
|
||||
import { register } from 'ol/proj/proj4'
|
||||
import proj4 from 'proj4'
|
||||
import { containsExtent } from 'ol/extent'
|
||||
import { drawingLayerStyle, regionsLayerStyle, selectStyle } from './MapStyles'
|
||||
import { googleMapsSatelliteSource, regionsLayerSource, yandexMapsSatelliteSource } from './MapSources'
|
||||
import { mapCenter, mapExtent } from './MapConstants'
|
||||
|
||||
const MapComponent = () => {
|
||||
proj4.defs('EPSG:3395', '+proj=merc +lon_0=0 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs')
|
||||
register(proj4);
|
||||
const yandexProjection = get('EPSG:3395')?.setExtent([-20037508.342789244, -20037508.342789244, 20037508.342789244, 20037508.342789244]) || 'EPSG:3395'
|
||||
|
||||
|
||||
const mapElement = useRef<HTMLDivElement | null>(null)
|
||||
const [currentTool, setCurrentTool] = useState<Type | null>(null)
|
||||
|
||||
@ -37,18 +25,9 @@ const MapComponent = () => {
|
||||
|
||||
const [satMapsProvider, setSatMapsProvider] = useState<SatelliteMapsProvider>('yandex')
|
||||
|
||||
const gMapsSatSource = useRef<XYZ>(new XYZ({
|
||||
//url: `https://khms2.google.com/kh/v=984?x={x}&y={y}&z={z}`,
|
||||
url: `http://localhost:5000/tile/google/{z}/{x}/{y}`,
|
||||
attributions: 'Map data © Google'
|
||||
}))
|
||||
const gMapsSatSource = useRef<XYZ>(googleMapsSatelliteSource)
|
||||
|
||||
const yMapsSatSource = useRef<XYZ>(new XYZ({
|
||||
//url: `https://sat0{1-4}.maps.yandex.net/tiles?l=sat&x={x}&y={y}&z={z}&scale=1&lang=ru_RU&client_id=yandex-web-maps`,
|
||||
url: `https://core-sat.maps.yandex.net/tiles?l=sat&x={x}&y={y}&z={z}&scale=1&lang=ru_RU`,
|
||||
attributions: 'Map data © Yandex',
|
||||
projection: yandexProjection,
|
||||
}))
|
||||
const yMapsSatSource = useRef<XYZ>(yandexMapsSatelliteSource)
|
||||
|
||||
const satLayer = useRef<TileLayer>(new TileLayer({
|
||||
source: gMapsSatSource.current,
|
||||
@ -56,60 +35,27 @@ const MapComponent = () => {
|
||||
|
||||
const draw = useRef<Draw | null>(null)
|
||||
const snap = useRef<Snap | null>(null)
|
||||
const selectFeature = useRef<Select | null>(null)
|
||||
|
||||
const selectFeature = useRef<Select>(new Select({
|
||||
condition: function (mapBrowserEvent) {
|
||||
return click(mapBrowserEvent) && shiftKeyOnly(mapBrowserEvent);
|
||||
},
|
||||
}))
|
||||
|
||||
const drawingLayer = useRef<VectorLayer | null>(null)
|
||||
const drawingLayerSource = useRef<VectorSource>(new VectorSource())
|
||||
|
||||
const regionsLayer = useRef<VectorLayer>(new VectorLayer({
|
||||
source: new VectorSource({
|
||||
url: 'sakha_republic.geojson',
|
||||
format: new GeoJSON(),
|
||||
}),
|
||||
style: new Style({
|
||||
stroke: new Stroke({
|
||||
color: 'blue',
|
||||
width: 1,
|
||||
}),
|
||||
fill: new Fill({
|
||||
color: 'rgba(0, 0, 255, 0.1)',
|
||||
}),
|
||||
}),
|
||||
source: regionsLayerSource,
|
||||
style: regionsLayerStyle
|
||||
}))
|
||||
|
||||
const selectStyle = new Style({
|
||||
fill: new Fill({
|
||||
color: 'rgba(0, 0, 255, 0.3)',
|
||||
}),
|
||||
stroke: new Stroke({
|
||||
color: 'rgba(255, 255, 255, 0.7)',
|
||||
width: 2,
|
||||
}),
|
||||
})
|
||||
|
||||
const selectedRegion = useRef<Feature | null>(null)
|
||||
|
||||
const geoLayer = useRef<VectorLayer | null>(null)
|
||||
|
||||
const geoLayerSource = useRef<VectorSource>(new VectorSource({
|
||||
url: 'https://openlayers.org/data/vector/ecoregions.json',
|
||||
format: new GeoJSON(),
|
||||
const baseLayer = useRef<TileLayer>(new TileLayer({
|
||||
source: new OSM(),
|
||||
}))
|
||||
|
||||
const baseLayer = useRef<TileLayer | null>(null)
|
||||
|
||||
const geoLayerStyle: FlatStyleLike = {
|
||||
'fill-color': ['string', ['get', 'COLOR'], '#eee'],
|
||||
}
|
||||
|
||||
const drawingLayerStyle: FlatStyleLike = {
|
||||
'fill-color': 'rgba(255, 255, 255, 0.2)',
|
||||
'stroke-color': '#ffcc33',
|
||||
'stroke-width': 2,
|
||||
'circle-radius': 7,
|
||||
'circle-fill-color': '#ffcc33',
|
||||
}
|
||||
|
||||
const addInteractions = () => {
|
||||
if (currentTool) {
|
||||
draw.current = new Draw({
|
||||
@ -156,60 +102,43 @@ const MapComponent = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const zoomToFeature = (feature: Feature) => {
|
||||
const geometry = feature.getGeometry()
|
||||
const extent = geometry?.getExtent()
|
||||
|
||||
if (map.current && extent) {
|
||||
map.current.getView().fit(extent, {
|
||||
duration: 300,
|
||||
maxZoom: 19,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
geoLayer.current = new VectorLayer({
|
||||
background: '#1a2b39',
|
||||
source: geoLayerSource.current,
|
||||
style: geoLayerStyle,
|
||||
})
|
||||
|
||||
baseLayer.current = new TileLayer({
|
||||
source: new OSM(),
|
||||
})
|
||||
|
||||
drawingLayer.current = new VectorLayer({
|
||||
source: drawingLayerSource.current,
|
||||
style: drawingLayerStyle,
|
||||
})
|
||||
|
||||
// Center coordinates of Yakutia in EPSG:3857
|
||||
const center = transform([129.7578941, 62.030804], 'EPSG:4326', 'EPSG:3857')
|
||||
|
||||
// Extent for Yakutia in EPSG:4326
|
||||
const extent4326 = [105.0, 55.0, 170.0, 75.0] // Approximate bounding box
|
||||
// Transform extent to EPSG:3857
|
||||
const extent = transformExtent(extent4326, 'EPSG:4326', 'EPSG:3857')
|
||||
|
||||
map.current = new Map({
|
||||
layers: [baseLayer.current, satLayer.current, regionsLayer.current, drawingLayer.current],
|
||||
target: mapElement.current as HTMLDivElement,
|
||||
view: new View({
|
||||
center,
|
||||
center: mapCenter,
|
||||
zoom: 2,
|
||||
extent: [
|
||||
11388546.533293726,
|
||||
7061866.113051185,
|
||||
18924313.434856508,
|
||||
13932243.11199202
|
||||
],
|
||||
maxZoom: 21,
|
||||
extent: mapExtent,
|
||||
}),
|
||||
})
|
||||
|
||||
const modify = new Modify({ source: drawingLayerSource.current })
|
||||
map.current.addInteraction(modify)
|
||||
|
||||
selectFeature.current = new Select({
|
||||
condition: function (mapBrowserEvent) {
|
||||
return click(mapBrowserEvent) && shiftKeyOnly(mapBrowserEvent);
|
||||
},
|
||||
})
|
||||
map.current.addInteraction(selectFeature.current)
|
||||
|
||||
selectFeature.current.on('select', (e) => {
|
||||
const selectedFeatures = e.selected
|
||||
|
||||
console.log(selectedFeatures)
|
||||
|
||||
if (selectedFeatures.length > 0) {
|
||||
selectedFeatures.forEach((feature) => {
|
||||
drawingLayerSource.current?.removeFeature(feature)
|
||||
@ -219,26 +148,63 @@ const MapComponent = () => {
|
||||
|
||||
loadFeatures()
|
||||
|
||||
// Show current selected region
|
||||
map.current.on('pointermove', function (e) {
|
||||
if (selectedRegion.current !== null) {
|
||||
selectedRegion.current.setStyle(undefined)
|
||||
selectedRegion.current = null
|
||||
}
|
||||
|
||||
if (map.current && selectStyle !== null) {
|
||||
if (map.current) {
|
||||
map.current.forEachFeatureAtPixel(e.pixel, function (f) {
|
||||
selectedRegion.current = f as Feature
|
||||
|
||||
selectedRegion.current.setStyle(selectStyle)
|
||||
|
||||
if (f.get('district')) {
|
||||
setStatusText(f.get('district'))
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
map.current.on('click', function (e) {
|
||||
if (selectedRegion.current !== null) {
|
||||
selectedRegion.current = null
|
||||
}
|
||||
|
||||
if (map.current) {
|
||||
map.current.forEachFeatureAtPixel(e.pixel, function (f) {
|
||||
selectedRegion.current = f as Feature
|
||||
// Zoom to the selected feature
|
||||
zoomToFeature(selectedRegion.current)
|
||||
|
||||
return true
|
||||
});
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
// Hide regions layer when fully visible
|
||||
map.current.on('moveend', function () {
|
||||
const viewExtent = map.current?.getView().calculateExtent(map.current.getSize())
|
||||
const features = regionsLayer.current.getSource()?.getFeatures()
|
||||
|
||||
let isViewCovered = false
|
||||
|
||||
features?.forEach((feature: Feature) => {
|
||||
const featureExtent = feature?.getGeometry()?.getExtent()
|
||||
if (viewExtent && featureExtent) {
|
||||
if (containsExtent(featureExtent, viewExtent)) {
|
||||
isViewCovered = true
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
regionsLayer.current.setVisible(!isViewCovered)
|
||||
})
|
||||
|
||||
return () => {
|
||||
map?.current?.setTarget(undefined)
|
||||
}
|
||||
@ -259,8 +225,6 @@ const MapComponent = () => {
|
||||
|
||||
const [statusText, setStatusText] = useState('')
|
||||
|
||||
const selected = useRef<FeatureLike | null>(null)
|
||||
|
||||
// Visibility setting
|
||||
useEffect(() => {
|
||||
satLayer.current?.setOpacity(satelliteOpacity)
|
||||
@ -352,10 +316,14 @@ const MapComponent = () => {
|
||||
>
|
||||
<OpenWith />
|
||||
</IconButton>
|
||||
|
||||
<IconButton>
|
||||
<Straighten />
|
||||
</IconButton>
|
||||
</Stack>
|
||||
|
||||
<Box>
|
||||
<div id="map-container" ref={mapElement} style={{ width: '100%', height: '500px', maxHeight: '100%', position: 'relative', flexGrow: 1 }}></div>
|
||||
<div id="map-container" ref={mapElement} style={{ width: '100%', height: '600px', maxHeight: '100%', position: 'relative', flexGrow: 1 }}></div>
|
||||
</Box>
|
||||
|
||||
<Stack>
|
||||
|
9
client/src/components/map/MapConstants.ts
Normal file
9
client/src/components/map/MapConstants.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { transform } from "ol/proj"
|
||||
|
||||
const mapExtent = [11388546.533293726, 7061866.113051185, 18924313.434856508, 13932243.11199202]
|
||||
const mapCenter = transform([129.7578941, 62.030804], 'EPSG:4326', 'EPSG:3857')
|
||||
|
||||
export {
|
||||
mapExtent,
|
||||
mapCenter
|
||||
}
|
32
client/src/components/map/MapSources.ts
Normal file
32
client/src/components/map/MapSources.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import GeoJSON from "ol/format/GeoJSON";
|
||||
import { get } from "ol/proj";
|
||||
import { register } from "ol/proj/proj4";
|
||||
import { XYZ } from "ol/source";
|
||||
import VectorSource from "ol/source/Vector";
|
||||
import proj4 from "proj4";
|
||||
proj4.defs('EPSG:3395', '+proj=merc +lon_0=0 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs')
|
||||
register(proj4);
|
||||
|
||||
const yandexProjection = get('EPSG:3395')?.setExtent([-20037508.342789244, -20037508.342789244, 20037508.342789244, 20037508.342789244]) || 'EPSG:3395'
|
||||
|
||||
const googleMapsSatelliteSource = new XYZ({
|
||||
url: `${import.meta.env.VITE_API_EMS_URL}/tile/google/{z}/{x}/{y}`,
|
||||
attributions: 'Map data © Google'
|
||||
})
|
||||
|
||||
const yandexMapsSatelliteSource = new XYZ({
|
||||
url: `${import.meta.env.VITE_API_EMS_URL}/tile/yandex/{z}/{x}/{y}`,
|
||||
attributions: 'Map data © Yandex',
|
||||
projection: yandexProjection,
|
||||
})
|
||||
|
||||
const regionsLayerSource = new VectorSource({
|
||||
url: 'sakha_republic.geojson',
|
||||
format: new GeoJSON(),
|
||||
})
|
||||
|
||||
export {
|
||||
googleMapsSatelliteSource,
|
||||
yandexMapsSatelliteSource,
|
||||
regionsLayerSource
|
||||
}
|
38
client/src/components/map/MapStyles.ts
Normal file
38
client/src/components/map/MapStyles.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import Fill from "ol/style/Fill";
|
||||
import { FlatStyleLike } from "ol/style/flat";
|
||||
import Stroke from "ol/style/Stroke";
|
||||
import Style from "ol/style/Style";
|
||||
|
||||
const drawingLayerStyle: FlatStyleLike = {
|
||||
'fill-color': 'rgba(255, 255, 255, 0.2)',
|
||||
'stroke-color': '#ffcc33',
|
||||
'stroke-width': 2,
|
||||
'circle-radius': 7,
|
||||
'circle-fill-color': '#ffcc33',
|
||||
}
|
||||
|
||||
const selectStyle = new Style({
|
||||
fill: new Fill({
|
||||
color: 'rgba(0, 0, 255, 0.3)',
|
||||
}),
|
||||
stroke: new Stroke({
|
||||
color: 'rgba(255, 255, 255, 0.7)',
|
||||
width: 2,
|
||||
}),
|
||||
})
|
||||
|
||||
const regionsLayerStyle = new Style({
|
||||
stroke: new Stroke({
|
||||
color: 'blue',
|
||||
width: 1,
|
||||
}),
|
||||
fill: new Fill({
|
||||
color: 'rgba(0, 0, 255, 0.1)',
|
||||
}),
|
||||
})
|
||||
|
||||
export {
|
||||
drawingLayerStyle,
|
||||
selectStyle,
|
||||
regionsLayerStyle
|
||||
}
|
Reference in New Issue
Block a user