nguhmap/src/app/map.tsx
2025-09-25 21:07:24 +02:00

135 lines
4.0 KiB
TypeScript

import { MapContainer, Marker, Rectangle, TileLayer, Tooltip, Polyline, Polygon } from "react-leaflet";
import L, { Icon } from "leaflet";
export type CountryEntry = {
code2: string;
code3: string;
common_name: string;
name?: string;
ruler?: string | string[];
ruler_title?: string;
ruler_link?: string;
founded?: string;
capital?: string;
ung?: "MEMBER" | "OBSERVER" | "FORMER";
ung_joined?: string;
ung_demoted?: string;
ung_left?: string;
dissolved?: string | true;
dissolved_date?: string;
disputed?: string | true;
not_ngation?: true;
condominium?: string[]
};
export type POI = {
coordinates: string;
label: string;
id: number;
last_editor: number | string
};
export type Road = {
id: number;
path: string;
network: string;
name: string;
last_editor: number | string
}
export type CountryPart = {
id: number;
country: string;
shape: string;
last_editor: number | string;
}
export type RailLine = {
id: String;
path: String;
last_editor: number | string;
}
export type Settlement = {
id: number;
coordinates: string;
name: string;
last_editor: number | string
}
export type TransitStop = {
id: string;
coordinates: string;
last_editor: number | string;
}
export type MapData = {
countries: CountryEntry[];
parts: CountryPart[];
pois: POI[];
farms: POI[];
rails: RailLine[];
roads: Road[];
settlements: Settlement[];
stops: TransitStop[];
}
function find_entry(code: string, entries: CountryEntry[]) : CountryEntry|undefined {
return entries.find((it) => it.code2 === code || it.code3 === code)
}
function parseCoords(desc: string) : [number, number][] {
console.log(desc);
const foo = [...desc.matchAll(/\((-?\d+),(-?\d+)\)/g)]
return foo.map(it => [-+it[2] - .5, +it[1] + .5])
}
export default function Map(props: { data: MapData }) {
const { data } = props;
return (
<>
<MapContainer
id="map"
center={[0, 0]}
maxBounds={[[-8192, -8192], [8192, 8192]]}
crs={L.CRS.Simple}
zoom={0}>
<TileLayer
url="/tiles/{z}/{x}_{y}.png"
tileSize={512}
minZoom={-4}
maxNativeZoom={0} />
<Rectangle bounds={[[-8000, -8000], [8000, 8000]]} pathOptions={{ color: "#000", stroke: true, fill: false, weight: 2 }} />
{
data.pois.map(it =>
<Marker key={it.id} position={parseCoords(it.coordinates)[0]} icon={new Icon({iconUrl: "/legend/poi.svg", iconAnchor: [6, 6]})}>
<Tooltip>{it.label}</Tooltip>
</Marker>
)
}
{
data.settlements.map(it =>
<Marker key={it.id} position={parseCoords(it.coordinates)[0]} icon={new Icon({iconUrl: "/legend/city.svg", iconAnchor: [6, 6]})}>
<Tooltip>{it.name}</Tooltip>
</Marker>
)
}
{
data.roads.map(it =>
<Polyline key={it.id} positions={parseCoords(it.path)} stroke={true} color="#FFF">
<Tooltip>{find_entry(it.network, data.countries)?.common_name} {it.name}</Tooltip>
</Polyline>
)
}
{
data.parts.map(it =>
<Polygon key={it.id} positions={parseCoords(it.shape)} stroke={true} color="#A00" fill={true} fillOpacity={.5} fillColor="#A00">
<Tooltip position={parseCoords(it.shape)[0]}>{find_entry(it.country, data.countries)?.common_name}</Tooltip>
</Polygon>
)
}
{data.stops.map(it => <Marker key={it.id} position={parseCoords(it.coordinates)[0]}><Tooltip>{it.id}</Tooltip></Marker>)}
</MapContainer>
</>)
}