Jakob Miksch
en de

Random Geodata Generator

last updated 2024-07-28

Für die Softwareentwicklung benötige ich oft verschiedene Geodatensätze zum Testen. Bisher habe ich diese mit geojson.io oder QGIS erstellt oder Daten von OpenStreetMap oder Natural Earth heruntergeladen. Ich hatte jedoch einen Anwendungsfall, bei dem ich Daten aus bestimmten Gebieten benötigte. Und zwar idealerweise nicht nur in WGS84, sondern auch in der für die Region passenden UTM-Projektion. Da es meines Wissens kein solches Tool gibt, habe ich es selbst geschrieben. Der Code ist OpenSource auf github.com/JakobMiksch/random-geodata verfügbar.

Die Demo Anwendung kann unter jakobmiksch.github.io/random-geodata/ aufgerufen werden.

Demo der Anwendung

Mein erster Versuch

Das Tool ist als Webanwendung konzipiert, so dass es jederzeit ohne Installation mit einem Webbrowser genutzt werden kann. Als Frontend-Webframework habe ich Vue.js und für das Kartenrendering OpenLayers gewählt, da ich mit beiden bereits viel Erfahrung habe.

Es war mir wichtig, dass der Benutzer den gewünschten Bereich auf einer Karte manuell auswählen kann. Dies ist mit den Bordmitteln von OpenLayers problemlos möglich. In der vom Benutzer ausgewählten BBOX werden dann 100 zufällige Punkte generiert. OpenLayers kann diese als GeoJSON exportieren.

Da ich aber auch andere Ausgabeformate wie Shapefile oder GeoPackage ermöglichen wollte, habe ich mich auf die Suche nach Möglichkeiten gemacht, dies zu realisieren. Eine Möglichkeit wäre, die Konvertierung über ein Backend laufen zu lassen. Da ich aber die ganze Anwendung einfach halten wollte, wollte ich versuchen die Konvertierung über JavaScript Bibliotheken im Frontend zu lösen. Leider habe ich nur für das Shapefile die Bibliothek shp-write gefunden. Die Generierung von GeoPackage scheint zwar auch möglich zu sein, aber ich fand es relativ kompliziert.

Allerdings waren auch bei shp-write ein paar Tricks nötig um dieses zum Laufen zu bringen. Im folgenden mein Code (siehe auch hier) den ich dazu genutzt hatte:

const options: shpwrite.DownloadOptions & shpwrite.ZipOptions = {
  outputType: "blob",
  compression: "DEFLATE",
  types: {
    point: fileName.value,
  },
};
const shpArrayBuffer = await shpwrite.zip(
  pointFeatureCollection.value,
  options
);
const blob = new Blob([shpArrayBuffer], { type: "application/octet-stream" });

Weitere Formate und Koordinatensysteme

Bisher konnte ich in GeoJSON und Shapefile exportieren, aber ich wollte noch mehr Formate. Unter anderem um eine Alternative zu Shapefile aufzuzeigen (siehe hier). Als Koordinatensystem konnte ich ausschließlich WGS84 ausgeben. Mit OpenLayers wäre auch zusätzlich WebMercator möglich gewesen. Allerdings sollte man dieses CRS eigentlich nur zum Anzeigen und nicht zum Weiterverarbeiten von Geodaten verwenden.

All diese Anforderungen wären sicherlich auch mit anderen JavaScript-Bibliotheken umsetzbar gewesen, jedoch bin ich durch Zufall auf das Projekt gdal3.js gestoßen. Dieses stellt die GDAL/OGR Kommandozeilentools als WebAssembly (WASM) im WebBrowser zur Verfügung. Dadurch kann ein Großteil der Funktionalität, die das native GDAL/OGR bietet, clientseitig im Browser genutzt werden.

GDAL/OGR im Browser anzusprechen funktioniert auch über die Kommandozeilenschnittstelle der verschiedenen GDAL/OGR Tools. In meinem Fall ogr2ogr. Man übergibt ein String-Array als Argument und bekommt einen Blob mit dem entsprechenden Format zurück.

Konkret habe ich damit verschiedene Formate wie GeoPackage, Postgres Dump, FlatGeoBuf und GeoJSON erzeugt. Beim Shapefile-Export hatte ich allerdings Probleme, da natürlich mehrere Sidecar-Dateien erzeugt werden. Bei den Koordinatensystemen habe ich mich vorerst nur auf WGS84 und UTM konzentriert.

Da das UTM-Koordinatensystem vom jeweiligen Standort auf der Erde abhängt, habe ich eine Funktion geschrieben, die aus dem Koordinatensystem des aktuellen Standortes den entsprechenden EPSG-Code ableitet.

// inspired by https://stackoverflow.com/a/9188972
const utmZoneFromLongitude = (longitude: number) => (Math.floor((longitude + 180) / 6) % 60) + 1

// inspired by https://gis.stackexchange.com/a/375285
const getUtmEpsgCode = (utmZone: number, latitude: number) => {
  const digitOneAndTwo = '32'
  const isNorth = latitude > 0
  const digitThree = isNorth ? '6' : '7'
  const digitFourFive = String(utmZone)
  return digitOneAndTwo + digitThree + String(digitFourFive)
}

export const getUtmEpsgFromCoordinate = (coordinate: Coordinate) => {
  const [longitude, latitude] = coordinate
  const zone = utmZoneFromLongitude(longitude)
  return getUtmEpsgCode(zone, latitude)
}

Das Ergebniss ist eine einfach zu benutzende Webseite, mit der man zufällige Geodaten erzeugen kann. Ich habe noch weitere Ideen wie man die Anwendung verbessern kann. Diese sind als Issues im Repository vermerkt. Beitragende sind herzlich willkommen.

EmailGitHubMastodonTwitterLinkedInXING RSS (deutsch)
© 2024 Jakob Miksch • Impressum