mirror of
https://gitlab.com/PronounsPage/PronounsPage.git
synced 2025-09-22 12:03:25 -04:00
wip
This commit is contained in:
parent
bc61615066
commit
0ffc1852d3
@ -1,7 +1,9 @@
|
||||
<template>
|
||||
<div>
|
||||
<div id="map" :class="isDark ? 'dark' : ''"></div>
|
||||
{{ JSON.stringify(polygons) }}
|
||||
<div v-for="polygon in polygons">
|
||||
{{ JSON.stringify(polygon) }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -9,8 +11,7 @@
|
||||
<script lang="ts">
|
||||
import dark from '../plugins/dark.ts';
|
||||
import walsLanguages from '~/assets/languages.csv';
|
||||
import polygons from '~/assets/shapes.json';
|
||||
import newMexicoPolygon from '~/assets/de.json';
|
||||
import polygonsByLocale from '~/assets/map.json';
|
||||
import locales from '../locale/locales.ts';
|
||||
import { clearUrl } from '~/src/helpers.ts';
|
||||
import type { LocaleDescription } from '../locale/locales.ts';
|
||||
@ -38,49 +39,9 @@ declare module 'leaflet' {
|
||||
export default dark.extend({
|
||||
data() {
|
||||
return {
|
||||
inputCurve: null as any,
|
||||
curve: null as any,
|
||||
controls: [],
|
||||
polygons: newMexicoPolygon,
|
||||
polygons: {} as Record<string, L.Polygon[]>,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
polygons() {
|
||||
this.update();
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
update() {
|
||||
const inputPath = [];
|
||||
const polygonPath = [];
|
||||
for (const polygon of this.polygons) {
|
||||
if (polygon.length >= 1) {
|
||||
inputPath.push('M', polygon[0]);
|
||||
}
|
||||
for (let i = 1; i < polygon.length; i++) {
|
||||
inputPath.push('L', polygon[i]);
|
||||
}
|
||||
inputPath.push('Z');
|
||||
/*const middleOf = (a: [number, number], b: [number, number]) => {
|
||||
return [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2];
|
||||
};
|
||||
|
||||
if (polygon.length >= 3) {
|
||||
polygonPath.push('M', middleOf(polygon[0], polygon[1]));
|
||||
polygonPath.push('Q', polygon[1], middleOf(polygon[1], polygon[2]));
|
||||
|
||||
for (let i = 2; i < polygon.length - 1; i++) {
|
||||
polygonPath.push('T', middleOf(polygon[i], polygon[i + 1]));
|
||||
}
|
||||
|
||||
polygonPath.push('T', middleOf(polygon[polygon.length - 1], polygon[0]));
|
||||
polygonPath.push('T', middleOf(polygon[0], polygon[1]));
|
||||
}*/
|
||||
}
|
||||
// this.inputCurve.setPath(inputPath);
|
||||
this.curve.setPath(inputPath);
|
||||
},
|
||||
},
|
||||
async mounted() {
|
||||
const { default: L } = await import('leaflet');
|
||||
await import ('@elfalem/leaflet-curve');
|
||||
@ -95,29 +56,13 @@ export default dark.extend({
|
||||
const map = L.map('map', {
|
||||
attributionControl: false,
|
||||
worldCopyJump: true,
|
||||
center: [50, 12.8],
|
||||
zoom: 6,
|
||||
center: [47, 10],
|
||||
zoom: 4,
|
||||
minZoom: 2,
|
||||
maxZoom: 6,
|
||||
maxZoom: 7,
|
||||
maxBounds: [[-75, Number.NEGATIVE_INFINITY], [85, Number.POSITIVE_INFINITY]],
|
||||
});
|
||||
|
||||
map.addEventListener('click', (event) => {
|
||||
this.polygons[this.polygons.length - 1].push(toPoint(event.latlng));
|
||||
});
|
||||
|
||||
window.addEventListener('keydown', (event) => {
|
||||
switch (event.key) {
|
||||
case 'Enter':
|
||||
this.polygons.push([]);
|
||||
return false;
|
||||
case 'Backspace':
|
||||
this.polygons[this.polygons.length - 1].pop();
|
||||
event.preventDefault();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
L.control.attribution({ position: 'bottomright' })
|
||||
.addAttribution('© <a href="https://doi.org/10.5281/zenodo.7385533" target="_blank" title="Dryer, Matthew S. & Haspelmath, Martin (eds.) 2013. The World Atlas of Language Structures Online. Leipzig: Max Planck Institute for Evolutionary Anthropology.">WALS</a>')
|
||||
.addAttribution('<a href="https://wals.info" target="_blank">wals.info</a>')
|
||||
@ -141,18 +86,20 @@ export default dark.extend({
|
||||
},
|
||||
}).addTo(map);
|
||||
|
||||
this.inputCurve = L.curve([], {
|
||||
color: '#ffffff',
|
||||
opacity: 0.5,
|
||||
}).addTo(map);
|
||||
|
||||
this.curve = L.curve([], {
|
||||
color: '#971064',
|
||||
fill: true,
|
||||
fillColor: '#c71585',
|
||||
fillOpacity: 0.5,
|
||||
fillRule: 'nonzero',
|
||||
}).addTo(map);
|
||||
for (const [locale, polygons] of Object.entries(polygonsByLocale)) {
|
||||
const polygonActors = polygons.map((polygon) => {
|
||||
return L.polygon(polygon, {
|
||||
color: '#971064',
|
||||
weight: 1,
|
||||
fill: true,
|
||||
// fillColor: `hsl(${index * 360 / Object.values(polygonsByLocale).length}deg, 100%, 50%)`,
|
||||
fillColor: '#c71585',
|
||||
fillOpacity: 0.2,
|
||||
fillRule: 'evenodd',
|
||||
}).addTo(map);
|
||||
});
|
||||
this.polygons[locale] = polygonActors;
|
||||
}
|
||||
|
||||
/* for (let i = 0; i < this.polygons.length; i++) {
|
||||
for (let j = 0; j < this.polygons[i].length; j++) {
|
||||
@ -167,7 +114,7 @@ export default dark.extend({
|
||||
}
|
||||
}*/
|
||||
|
||||
/*for (const walsLanguage of walsLanguages) {
|
||||
for (const walsLanguage of walsLanguages) {
|
||||
if (!localesByWalsCode.hasOwnProperty(walsLanguage.id)) {
|
||||
continue;
|
||||
}
|
||||
@ -181,12 +128,18 @@ export default dark.extend({
|
||||
radius: 300000 * Math.cos(walsLanguage.latitude * Math.PI / 180), // compensate for Mercator projection
|
||||
}).addTo(map);
|
||||
circle.bindTooltip(`<strong>${locale.name}</strong><br/>${clearUrl(locale.url)}`, { opacity: 1 });
|
||||
circle.on('mouseover', () => {
|
||||
this.polygons[locale.code].map((polygon) => polygon.setStyle({ fillOpacity: 1 }));
|
||||
});
|
||||
circle.on('mouseout', () => {
|
||||
this.polygons[locale.code].map((polygon) => polygon.setStyle({ fillOpacity: 0.2 }));
|
||||
});
|
||||
circle.on('click', () => {
|
||||
window.open(locale.url);
|
||||
});
|
||||
}
|
||||
}*/
|
||||
this.update();
|
||||
}
|
||||
// this.update();
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@ -65,11 +65,13 @@
|
||||
"pageres": "^6.3.1",
|
||||
"papaparse": "^5.4.1",
|
||||
"plausible-api": "https://github.com/avo7/plausible-api.git#main",
|
||||
"polygon-clipping": "^0.15.7",
|
||||
"qr-code-styling": "^1.6.0-rc.1",
|
||||
"query-string": "^7.1.1",
|
||||
"querystringify": "^2.2.0",
|
||||
"rtlcss": "^3.1.2",
|
||||
"sha1": "^1.1.1",
|
||||
"simplify-js": "^1.2.4",
|
||||
"speakeasy": "^2.0.0",
|
||||
"sql-template-strings": "^2.2.2",
|
||||
"sqlite": "^4.0.12",
|
||||
|
25
pnpm-lock.yaml
generated
25
pnpm-lock.yaml
generated
@ -158,6 +158,9 @@ dependencies:
|
||||
plausible-api:
|
||||
specifier: https://github.com/avo7/plausible-api.git#main
|
||||
version: github.com/avo7/plausible-api/0bb79ad1d26754a71b3ec1351255dbf5a32e6e2a
|
||||
polygon-clipping:
|
||||
specifier: ^0.15.7
|
||||
version: 0.15.7
|
||||
qr-code-styling:
|
||||
specifier: ^1.6.0-rc.1
|
||||
version: 1.6.0-rc.1
|
||||
@ -173,6 +176,9 @@ dependencies:
|
||||
sha1:
|
||||
specifier: ^1.1.1
|
||||
version: 1.1.1
|
||||
simplify-js:
|
||||
specifier: ^1.2.4
|
||||
version: 1.2.4
|
||||
speakeasy:
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0
|
||||
@ -13146,6 +13152,13 @@ packages:
|
||||
- typescript
|
||||
dev: false
|
||||
|
||||
/polygon-clipping@0.15.7:
|
||||
resolution: {integrity: sha512-nhfdr83ECBg6xtqOAJab1tbksbBAOMUltN60bU+llHVOL0e5Onm1WpAXXWXVB39L8AJFssoIhEVuy/S90MmotA==}
|
||||
dependencies:
|
||||
robust-predicates: 3.0.2
|
||||
splaytree: 3.1.2
|
||||
dev: false
|
||||
|
||||
/posix-character-classes@0.1.1:
|
||||
resolution: {integrity: sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@ -14802,6 +14815,10 @@ packages:
|
||||
resolution: {integrity: sha512-fJhQQI5tLrQvYIYFpOnFinzv9dwmR7hRnUz1XqP3OJ1jIweTNOd6aTO4jwQSgcBSFUB+/KHJxuGneime+FdzOw==}
|
||||
dev: false
|
||||
|
||||
/robust-predicates@3.0.2:
|
||||
resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==}
|
||||
dev: false
|
||||
|
||||
/rrweb-cssom@0.6.0:
|
||||
resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==}
|
||||
dev: false
|
||||
@ -15178,6 +15195,10 @@ packages:
|
||||
is-arrayish: 0.3.2
|
||||
dev: true
|
||||
|
||||
/simplify-js@1.2.4:
|
||||
resolution: {integrity: sha512-vITfSlwt7h/oyrU42R83mtzFpwYk3+mkH9bOHqq/Qw6n8rtR7aE3NZQ5fbcyCUVVmuMJR6ynsAhOfK2qoah8Jg==}
|
||||
dev: false
|
||||
|
||||
/sirv@2.0.3:
|
||||
resolution: {integrity: sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA==}
|
||||
engines: {node: '>= 10'}
|
||||
@ -15352,6 +15373,10 @@ packages:
|
||||
base32.js: 0.0.1
|
||||
dev: false
|
||||
|
||||
/splaytree@3.1.2:
|
||||
resolution: {integrity: sha512-4OM2BJgC5UzrhVnnJA4BkHKGtjXNzzUfpQjCO8I05xYPsfS/VuQDwjCGGMi8rYQilHEV4j8NBqTFbls/PZEE7A==}
|
||||
dev: false
|
||||
|
||||
/split-on-first@1.1.0:
|
||||
resolution: {integrity: sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==}
|
||||
engines: {node: '>=6'}
|
||||
|
@ -1,19 +1,5 @@
|
||||
<template>
|
||||
<Page>
|
||||
<div class="list-group d-flex flex-wrap flex-row my-4">
|
||||
<a
|
||||
v-for="(options, locale) in $locales"
|
||||
:key="locale"
|
||||
:href="options.url"
|
||||
class="list-group-item list-group-item-action list-group-item-hoverable w-md-50"
|
||||
>
|
||||
<div class="h3">
|
||||
<LocaleIcon :locale="options" class="mx-2" />
|
||||
{{ options.name }}
|
||||
<small v-if="options.extra" class="text-muted">({{ options.extra }})</small>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<template #below>
|
||||
<LanguageMap />
|
||||
<p class="small text-muted my-3">
|
||||
|
@ -1,5 +1,9 @@
|
||||
import fs from 'fs';
|
||||
|
||||
import polygonClipping from 'polygon-clipping';
|
||||
import type { MultiPolygon, Polygon, Ring } from 'polygon-clipping';
|
||||
import simplify from 'simplify-js';
|
||||
|
||||
const __dirname = new URL('.', import.meta.url).pathname;
|
||||
|
||||
const getFilePath = (code: string, level: number): string => {
|
||||
@ -37,29 +41,37 @@ interface GeoJSONSubset {
|
||||
}[]
|
||||
}
|
||||
|
||||
const getCountryCode = (code: string): [string, 0 | 1 | 2] => {
|
||||
if (code.length === 3) {
|
||||
return [code, 0];
|
||||
} else {
|
||||
const level = code.length === 5 ? 1 : 2;
|
||||
let countryCode;
|
||||
switch (code.slice(0, 2)) {
|
||||
case 'BE': countryCode = 'BEL';
|
||||
break;
|
||||
case 'CH': countryCode = 'CHE';
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown ${countryCode}`);
|
||||
}
|
||||
return [countryCode, level];
|
||||
const getLevel = (region: string): 0 | 1 | 2 => {
|
||||
if (region === 'ALL') {
|
||||
return 0;
|
||||
} else if (region.length === 3 || region.length === 5) {
|
||||
return 1;
|
||||
}
|
||||
return 2;
|
||||
};
|
||||
|
||||
class CoordinatesFetcher {
|
||||
adm0: Record<string, string> = {};
|
||||
adm1: Record<string, string> = {};
|
||||
adm2: Record<string, string> = {};
|
||||
coordinatesByCode: Record<string, [number, number][][]> = {};
|
||||
coordinatesByCode: Record<string, Record<string, MultiPolygon>> = {};
|
||||
|
||||
async prime(codes: Record<string, Record<string, string[]>>) {
|
||||
const neededLevelsByCountryCode: Record<string, Set<0 | 1 | 2>> = {};
|
||||
for (const a of Object.values(codes)) {
|
||||
for (const [countryCode, regionCodes] of Object.entries(a)) {
|
||||
if (!Object.hasOwn(neededLevelsByCountryCode, countryCode)) {
|
||||
neededLevelsByCountryCode[countryCode] = new Set();
|
||||
}
|
||||
for (const regionCode of regionCodes) {
|
||||
neededLevelsByCountryCode[countryCode].add(getLevel(regionCode));
|
||||
}
|
||||
}
|
||||
}
|
||||
await Promise.all(Object.entries(neededLevelsByCountryCode).flatMap(([countryCode, levels]) => {
|
||||
return [...levels].map((level) => this.fetchCoordinates(countryCode, level));
|
||||
}));
|
||||
}
|
||||
|
||||
async getGeoJSONLink(code: string, level: number): Promise<string> {
|
||||
if (level === 0) {
|
||||
@ -84,36 +96,40 @@ class CoordinatesFetcher {
|
||||
const path = getFilePath(countryCode, level);
|
||||
if (!fs.existsSync(path)) {
|
||||
const response = await fetch(await this.getGeoJSONLink(countryCode, level));
|
||||
fs.writeFileSync(path, await response.text());
|
||||
await fs.promises.writeFile(path, await response.text());
|
||||
}
|
||||
const data = JSON.parse(fs.readFileSync(path, 'utf-8')) as GeoJSONSubset;
|
||||
const data = JSON.parse(await fs.promises.readFile(path, 'utf-8')) as GeoJSONSubset;
|
||||
const entries = data.features.map((feature) => {
|
||||
let coordinates;
|
||||
switch (feature.geometry.type) {
|
||||
case 'Polygon':
|
||||
if (feature.geometry.coordinates.length !== 1) {
|
||||
process.stderr.write(`Assertion failed on ${feature.properties.shapeISO} (Polygon)\n`);
|
||||
}
|
||||
return [feature.properties.shapeISO, feature.geometry.coordinates] as const;
|
||||
coordinates = [feature.geometry.coordinates];
|
||||
break;
|
||||
case 'MultiPolygon':
|
||||
if (feature.geometry.coordinates[0].length !== 1) {
|
||||
process.stderr.write(`Assertion failed on ${feature.properties.shapeISO} (MultiPolygon)\n`);
|
||||
}
|
||||
return [feature.properties.shapeISO, feature.geometry.coordinates.flatMap((a) => a)] as const;
|
||||
coordinates = feature.geometry.coordinates;
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown type ${countryCode}`);
|
||||
}
|
||||
return [feature.properties.shapeISO, coordinates] as const;
|
||||
});
|
||||
for (const [code, coordinates] of entries) {
|
||||
this.coordinatesByCode[code] = coordinates;
|
||||
if (!Object.hasOwn(this.coordinatesByCode, countryCode)) {
|
||||
this.coordinatesByCode[countryCode] = {};
|
||||
}
|
||||
for (const [regionCode, coordinates] of entries) {
|
||||
this.coordinatesByCode[countryCode][regionCode] = coordinates;
|
||||
}
|
||||
}
|
||||
|
||||
async getCoordinates(code: string): Promise<[number, number][][]> {
|
||||
if (!Object.hasOwn(this.coordinatesByCode, code)) {
|
||||
const [countryCode, level] = getCountryCode(code);
|
||||
await this.fetchCoordinates(countryCode, level);
|
||||
getCoordinates(countryCode: string, regionCode: string): MultiPolygon {
|
||||
if (regionCode === 'ALL') {
|
||||
regionCode = countryCode;
|
||||
}
|
||||
return this.coordinatesByCode[code];
|
||||
const coordinates = this.coordinatesByCode[countryCode][regionCode];
|
||||
if (coordinates === undefined) {
|
||||
throw new Error(`Cannot find ${countryCode} ${regionCode}`);
|
||||
}
|
||||
return coordinates;
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,33 +142,145 @@ const transformCoordinate = (coordinate: [number, number]): [number, number] =>
|
||||
return [round(coordinate[1]), round(coordinate[0])];
|
||||
};
|
||||
|
||||
const countCoordinates = (polygons: [number, number][][]): number => {
|
||||
return polygons.reduce((count, polygon) => count + polygon.length, 0);
|
||||
const countCoordinates = (polygons: MultiPolygon): number => {
|
||||
return polygons.flatMap((polygon) => polygon).reduce((count, ring) => count + ring.length, 0);
|
||||
};
|
||||
|
||||
const makePolygons = async (fetcher: CoordinatesFetcher, code: string): Promise<[number, number][][]> => {
|
||||
const original = await fetcher.getCoordinates(code);
|
||||
const makePolygons = (fetcher: CoordinatesFetcher, countryCode: string, regionCode: string): Polygon => {
|
||||
const original = fetcher.getCoordinates(countryCode, regionCode);
|
||||
const transformed = original.map((polygon) => {
|
||||
return polygon.map(transformCoordinate).reduce((acc, cur) => {
|
||||
// process.stdout.write(`${JSON.stringify(acc[acc.length - 1])} ${JSON.stringify(cur)}\n`);
|
||||
if (acc.length >= 2 && (acc[acc.length - 2][0] === cur[0] && acc[acc.length - 1][0] === cur[0] || acc[acc.length - 2][1] === cur[1] && acc[acc.length - 1][1] === cur[1])) {
|
||||
acc.pop();
|
||||
return polygon.map((ring) => ring.map(transformCoordinate));
|
||||
/* return polygon.map((ring) => {
|
||||
const transformed = ring.map(transformCoordinate).reduce((acc, cur) => {
|
||||
// process.stdout.write(`${JSON.stringify(acc[acc.length - 1])} ${JSON.stringify(cur)}\n`);
|
||||
if (acc.length >= 2 && (acc[acc.length - 2][0] === cur[0] && acc[acc.length - 1][0] === cur[0] || acc[acc.length - 2][1] === cur[1] && acc[acc.length - 1][1] === cur[1])) {
|
||||
acc.pop();
|
||||
}
|
||||
if (acc.length === 0 || !pointEquals(acc[acc.length - 1], cur)) {
|
||||
// process.stdout.write('push\n');
|
||||
acc.push(cur);
|
||||
}
|
||||
return acc;
|
||||
}, [] as [number, number][]);
|
||||
if (transformed.length >= 2 && pointEquals(transformed[0], transformed[transformed.length - 1])) {
|
||||
transformed.pop();
|
||||
}
|
||||
if (acc.length === 0 || acc[acc.length - 1][0] !== cur[0] || acc[acc.length - 1][1] !== cur[1]) {
|
||||
// process.stdout.write('push\n');
|
||||
acc.push(cur);
|
||||
}
|
||||
return acc;
|
||||
}, [] as [number, number][]);
|
||||
return transformed;
|
||||
});*/
|
||||
});
|
||||
process.stdout.write(`${code} has ${countCoordinates(original)} original and ${countCoordinates(transformed)} transformed coordinates\n`);
|
||||
// process.stdout.write(`${countryCode} ${regionCode} has ${countCoordinates(original)} original and ${countCoordinates(transformed)} transformed coordinates\n`);
|
||||
return transformed;
|
||||
};
|
||||
|
||||
const writePolygons = async (fetcher: CoordinatesFetcher, locale: string, codes: string[]): Promise<void> => {
|
||||
const result = await Promise.all(codes.map((code) => makePolygons(fetcher, code)));
|
||||
const polygons = result.flatMap((polygons) => polygons);
|
||||
fs.writeFileSync(`${__dirname}/../assets/${locale}.json`, JSON.stringify(polygons));
|
||||
const pointEquals = (pointA: [number, number] | undefined, pointB: [number, number] | undefined): boolean => {
|
||||
return pointA !== undefined && pointB !== undefined && pointA[0] === pointB[0] && pointA[1] === pointB[1];
|
||||
};
|
||||
|
||||
const pointNear = (pointA: [number, number] | undefined, pointB: [number, number] | undefined): boolean => {
|
||||
const distance = 0.1;
|
||||
return pointA !== undefined && pointB !== undefined && Math.sqrt((pointA[0] - pointB[0]) ** 2 + (pointA[1] - pointB[1]) ** 2) <= distance;
|
||||
};
|
||||
|
||||
const wrappingIndex = <T>(array: T[], index: number): T => {
|
||||
return array[(index % array.length + array.length) % array.length];
|
||||
};
|
||||
|
||||
const tryMergePolygons = (polygonA: [number, number][], polygonB: [number, number][]): [number, number][] | undefined => {
|
||||
for (let i = 0; i < polygonA.length; i++) {
|
||||
for (let j = 0; j < polygonB.length; j++) {
|
||||
if (pointNear(polygonA[i], polygonB[j])) {
|
||||
if (pointNear(polygonA[i + 1], polygonB[j + 1])) {
|
||||
console.log(`found a match forwards ${i} ${j}`);
|
||||
}
|
||||
if (pointNear(polygonA[i + 1], polygonB[j - 1])) {
|
||||
console.log(`found a match backwards ${i} (of ${polygonA.length}) ${j} (of ${polygonB.length})`);
|
||||
let k = 2;
|
||||
while (pointNear(polygonA[i + k], wrappingIndex(polygonB, j - k)) && k < polygonA.length) {
|
||||
k++;
|
||||
}
|
||||
console.log(`length ${k}`);
|
||||
/* console.log(`neighbors A: ${JSON.stringify(polygonA.slice(i - 2, i + 1))} ${JSON.stringify(polygonB.slice(j, j + 3))}`);
|
||||
console.log(`neighbors B: ${JSON.stringify(polygonB.slice(j - k + 2, j - k + 1))} ${JSON.stringify(polygonA.slice(i + k - 1, i + k + 2))}`);
|
||||
|
||||
console.log(polygonA.slice(0, i));
|
||||
console.log(polygonB.slice(j, polygonB.length));
|
||||
console.log(polygonB.slice(j - k + 5, j - k));
|
||||
console.log(polygonA.slice(i + k - 1, i + k + 4));*/
|
||||
return [
|
||||
...polygonA.slice(0, i),
|
||||
...polygonB.slice(j, polygonB.length),
|
||||
...polygonB.slice(0, j - k),
|
||||
...polygonA.slice(i + k - 1, polygonA.length),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* const mergePolygons = (polygons: MultiPolygon): MultiPolygon=> {
|
||||
return union(polygons);
|
||||
for (let i = 0; i < polygons.length; i++) {
|
||||
for (let j = i + 1; j < polygons.length; j++) {
|
||||
// console.log(`trying to merge ${i} ${j}`);
|
||||
const merged = tryMergePolygons(polygons[i], polygons[j]);
|
||||
if (merged) {
|
||||
console.log(`merged ${i} and ${j}`);
|
||||
polygons[i] = merged;
|
||||
polygons.splice(j, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return polygons;
|
||||
};
|
||||
|
||||
const tidyPolygon = (polygon: [number, number][]) => {
|
||||
for (let i = 0; i < polygon.length; i++) {
|
||||
for (let j = i + 1; j < polygon.length; j++) {
|
||||
if (pointEquals(polygon[i], polygon[j])) {
|
||||
if (pointEquals(polygon[i + 1], polygon[j + 1])) {
|
||||
/* console.log(`found a tidy match forwards ${i} ${j}`);
|
||||
let k = 2;
|
||||
while (pointEquals(polygon[i + k], polygon[(j + k) % polygon.length]) && k < polygon.length) {
|
||||
k++;
|
||||
}
|
||||
polygon.splice(j, k);
|
||||
continue;*
|
||||
}
|
||||
if (pointEquals(polygon[i + 1], polygon[j - 1])) {
|
||||
/* console.log(`found a tidy match backwards ${i} ${j}`);
|
||||
let k = 2;
|
||||
while (pointEquals(polygon[i + k], wrappingIndex(polygon, j + k)) && k < polygon.length) {
|
||||
k++;
|
||||
}
|
||||
console.log(i, j, k, j - i);
|
||||
console.log(polygon.slice(i - 3, i + 4));
|
||||
console.log(polygon.slice(j - 3, j + 4));
|
||||
// polygon.splice(j, k);
|
||||
continue;*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return polygon;
|
||||
};
|
||||
*/
|
||||
|
||||
const simplifyPolygon = (polygon: Polygon): Ring => {
|
||||
const ring = polygon[0].map(([x, y]) => ({ x, y }));
|
||||
const simplified = simplify(ring, 0.05);
|
||||
return simplified.map(({ x, y }) => [x, y]);
|
||||
};
|
||||
|
||||
const writePolygons = async (fetcher: CoordinatesFetcher, locales: Record<string, Record<string, string[]>>): Promise<void> => {
|
||||
const entries = Object.entries(locales).map(([locale, codes]) => {
|
||||
let polygons = Object.entries(codes)
|
||||
.flatMap(([code, regions]) => regions.flatMap((region) => makePolygons(fetcher, code, region)));
|
||||
polygons = polygonClipping.union(polygons).map(simplifyPolygon);
|
||||
// polygons = polygonClipping.union(polygons).map(simplifyPolygon);
|
||||
return [locale, polygons];
|
||||
});
|
||||
fs.writeFileSync(`${__dirname}/../assets/map.json`, JSON.stringify(Object.fromEntries(entries)));
|
||||
};
|
||||
|
||||
const main = async (): Promise<void> => {
|
||||
@ -160,8 +288,40 @@ const main = async (): Promise<void> => {
|
||||
fs.mkdirSync(`${__dirname}/../assets/geo/`);
|
||||
}
|
||||
const fetcher = new CoordinatesFetcher();
|
||||
await fetcher.fetchCoordinates('BEL', 3);
|
||||
await writePolygons(fetcher, 'de', ['POL', 'AUT', 'BE-WLG', 'DEU', 'LIE', 'LUX', 'CH-AG', 'CH-AR', 'CH-AI', 'CH-BL', 'CH-BS', 'CH-BE', 'CH-FR', 'CH-GL', 'CH-GR', 'CH-LU', 'CH-NW', 'CH-OW', 'CH-SG', 'CH-SH', 'CH-SO', 'CH-SZ', 'CH-TG', 'CH-UR', 'CH-VS', 'CH-ZG', 'CH-ZH']);
|
||||
const codes = {
|
||||
de: {
|
||||
AUT: ['ALL'],
|
||||
BEL: ['BE-WLG'],
|
||||
DEU: ['ALL'],
|
||||
LIE: ['ALL'],
|
||||
LUX: ['ALL'],
|
||||
CHE: ['CH-AG', 'CH-AR', 'CH-AI', 'CH-BL', 'CH-BS', 'CH-BE', 'CH-FR', 'CH-GL', 'CH-GR', 'CH-LU', 'CH-NW', 'CH-OW', 'CH-SG', 'CH-SH', 'CH-SO', 'CH-SZ', 'CH-TG', 'CH-UR', 'CH-VS', 'CH-ZG', 'CH-ZH'],
|
||||
},
|
||||
en: {
|
||||
AUS: ['ALL'],
|
||||
GBR: ['ALL'],
|
||||
IRL: ['ALL'],
|
||||
USA: ['ALL'],
|
||||
},
|
||||
es: {
|
||||
ESP: ['ALL'],
|
||||
},
|
||||
fr: {
|
||||
BEL: ['BRU', 'WAL'],
|
||||
CHE: ['CH-BE', 'CH-FR', 'CH-GE', 'CH-JU', 'CH-NE', 'CH-VS', 'CH-VD'],
|
||||
FRA: ['ALL'],
|
||||
LUX: ['ALL'],
|
||||
},
|
||||
nl: {
|
||||
BEL: ['BRU', 'VLG'],
|
||||
NLD: ['ALL'],
|
||||
},
|
||||
pl: {
|
||||
POL: ['ALL'],
|
||||
},
|
||||
};
|
||||
await fetcher.prime(codes);
|
||||
await writePolygons(fetcher, codes);
|
||||
};
|
||||
|
||||
await main();
|
||||
|
Loading…
x
Reference in New Issue
Block a user