demarches-normaliennes/app/javascript/components/MapEditor/readGeoFile.ts
2022-09-01 09:32:50 +02:00

98 lines
2.8 KiB
TypeScript

import { gpx, kml } from '@tmcw/togeojson';
import type { FeatureCollection, Feature, Geometry } from 'geojson';
import { generateId } from '../shared/maplibre/utils';
export function readGeoFile(
file: File
): Promise<FeatureCollection & { filename: string }> {
const reader = new FileReader();
return new Promise((resolve, reject) => {
reader.onload = (event: FileReaderEventMap['load']) => {
const content = event.target?.result;
if (typeof content == 'string') {
const featureCollection = parse(content, file.name);
resolve(normalizeFeatureCollection(featureCollection, file.name));
} else {
reject(new Error('Invalid file content'));
}
};
reader.readAsText(file, 'UTF-8');
});
}
function parse(
content: string,
filename: string
): FeatureCollection<Geometry | null> {
const isGpxFile = filename.includes('.gpx');
const xml = new DOMParser().parseFromString(content, 'text/xml');
return isGpxFile ? gpx(xml) : kml(xml);
}
function normalizeFeatureCollection(
featureCollection: FeatureCollection<Geometry | null>,
filename: string
): FeatureCollection & { filename: string } {
const sourceFilename = `${generateId()}-${filename}`;
const features = featureCollection.features
.filter(isFeatureWithGeometry)
.flatMap((feature) => normalizeFeature(feature, sourceFilename));
return {
type: 'FeatureCollection',
features,
filename: sourceFilename
};
}
function isFeatureWithGeometry(
feature: Feature<Geometry | null>
): feature is Feature {
return feature.geometry !== null;
}
function normalizeFeature(feature: Feature, filename?: string): Feature[] {
switch (feature.geometry.type) {
case 'MultiPoint':
return feature.geometry.coordinates.map((coordinates) => ({
type: 'Feature',
geometry: {
type: 'Point',
coordinates
},
properties: { ...feature.properties, filename }
}));
case 'MultiLineString':
return feature.geometry.coordinates.map((coordinates) => ({
type: 'Feature',
geometry: {
type: 'LineString',
coordinates
},
properties: { ...feature.properties, filename }
}));
case 'MultiPolygon':
return feature.geometry.coordinates.map((coordinates) => ({
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates
},
properties: { ...feature.properties, filename }
}));
case 'GeometryCollection':
return feature.geometry.geometries.map((geometry) => ({
type: 'Feature',
geometry,
properties: { ...feature.properties, filename }
}));
default:
return [
{
...feature,
properties: { ...feature.properties, filename }
}
];
}
}