135 lines
4.0 KiB
TypeScript
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>
|
|
|
|
</>)
|
|
} |