126 lines
4.5 KiB
TypeScript
126 lines
4.5 KiB
TypeScript
import { useEffect, useRef } from 'react'
|
|
import 'ol/ol.css'
|
|
import { Container, Stack, Tabs } from '@mantine/core'
|
|
import OlMap from 'ol/Map'
|
|
import { v4 as uuidv4 } from 'uuid'
|
|
import TileLayer from 'ol/layer/Tile'
|
|
import { OSM } from 'ol/source'
|
|
import View from 'ol/View'
|
|
import { transform } from 'ol/proj'
|
|
import VectorLayer from 'ol/layer/Vector'
|
|
import VectorSource from 'ol/source/Vector'
|
|
import Feature from 'ol/Feature'
|
|
import { LineString } from 'ol/geom'
|
|
import { Stroke, Style, Text } from 'ol/style'
|
|
|
|
const center = [14443331.466543002, 8878970.176309839]
|
|
|
|
const style = new Style({
|
|
stroke: new Stroke({ color: 'blue', width: 2 }),
|
|
text: new Text({
|
|
font: 'bold 14px',
|
|
placement: 'line',
|
|
offsetY: -14
|
|
}),
|
|
})
|
|
|
|
const MapLineTest = () => {
|
|
const lines = [
|
|
{ name: 'A', points: [[100, 100], [200, 200]] },
|
|
{ name: 'B', points: [[200, 200], [300, 200]] },
|
|
{ name: 'X', points: [[200, 200], [300, 300]] },
|
|
{ name: 'L', points: [[300, 300], [350, 300]] },
|
|
{ name: 'N', points: [[300, 300], [250, 300]] },
|
|
{ name: 'I', points: [[300, 200], [300, 100]] },
|
|
{ name: 'J', points: [[300, 100], [250, 50]] },
|
|
{ name: 'C', points: [[300, 200], [400, 150]] },
|
|
{ name: 'D', points: [[400, 150], [400, 100]] },
|
|
]
|
|
|
|
const map = useRef<OlMap | null>(null)
|
|
const vectorLayer = useRef(new VectorLayer({
|
|
source: new VectorSource(),
|
|
style: feature => {
|
|
style.getText()?.setText(feature.get('label'))
|
|
return style
|
|
}
|
|
}))
|
|
|
|
const mapElement = useRef<HTMLDivElement | null>(null)
|
|
|
|
useEffect(() => {
|
|
map.current = new OlMap({
|
|
controls: [],
|
|
layers: [
|
|
new TileLayer({ source: new OSM(), properties: { id: uuidv4(), name: 'OpenStreetMap' } }),
|
|
vectorLayer.current
|
|
]
|
|
})
|
|
map.current.setTarget(mapElement.current as HTMLDivElement)
|
|
map.current.setView(new View({
|
|
center: transform([129.7466541, 62.083504], 'EPSG:4326', 'EPSG:3857'),
|
|
zoom: 16,
|
|
maxZoom: 21,
|
|
}))
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
if (map.current) {
|
|
const graph = new Map<string, { node: string, distance: number }[]>()
|
|
|
|
// build graph adjacency list
|
|
lines.forEach(({ points }) => {
|
|
const [start, end] = points.map(pt => pt.join(','))
|
|
const distance = Math.hypot(points[1][0] - points[0][0], points[1][1] - points[0][1])
|
|
|
|
if (!graph.has(start)) graph.set(start, [])
|
|
if (!graph.has(end)) graph.set(end, [])
|
|
|
|
graph.get(start)?.push({ node: end, distance })
|
|
graph.get(end)?.push({ node: start, distance })
|
|
})
|
|
|
|
// perform DFS to calculate distances from "A"
|
|
const startNode = lines[0].points[0].join(',')
|
|
const distances = new Map<string, number>()
|
|
const dfs = (node: string, currentDist: number) => {
|
|
if (distances.has(node) && distances.get(node)! <= currentDist) return
|
|
distances.set(node, currentDist)
|
|
for (const { node: neighbor, distance } of graph.get(node) || []) {
|
|
dfs(neighbor, currentDist + distance)
|
|
}
|
|
}
|
|
dfs(startNode, 0)
|
|
|
|
// render features
|
|
lines.forEach(({ name, points }) => {
|
|
const lineCoords = points.map(point => [point[0] + center[0], point[1] + center[1]])
|
|
const feature = new Feature(new LineString(lineCoords))
|
|
|
|
const lastNode = points[1].join(',')
|
|
const label = `${name} ${feature.getGeometry()?.getLength().toFixed(2)} (${distances.get(lastNode)?.toFixed(1) ?? '∞'})`
|
|
|
|
feature.set('label', label)
|
|
vectorLayer.current.getSource()?.addFeature(feature)
|
|
})
|
|
}
|
|
}, [])
|
|
|
|
return (
|
|
<Container fluid w='100%' pos='relative' p={0}>
|
|
<Tabs h='100%' variant='default' value={'map'} keepMounted={true}>
|
|
<Stack gap={0} h='100%'>
|
|
<Tabs.List>
|
|
<Tabs.Tab value={'map'}>Map</Tabs.Tab>
|
|
</Tabs.List>
|
|
<Tabs.Panel value={'map'} h='100%' pos='relative'>
|
|
<Container pos='absolute' fluid p={0} w='100%' h='100%' ref={mapElement}></Container>
|
|
</Tabs.Panel>
|
|
</Stack>
|
|
</Tabs>
|
|
</Container>
|
|
)
|
|
}
|
|
|
|
export default MapLineTest
|