Random Geodata Generator
last updated 2024-07-28
For software development, I often need different geodata sets for testing. So far, I have created these with geojson.io or QGIS or downloaded data from OpenStreetMap or Natural Earth. However, I had a use case where I needed data from specific regions. And ideally not only in WGS84, but also in the respective UTM projection for the region. Since there is no such tool to my knowledge, I wrote it myself. The code is available as open source on github.com/JakobMiksch/random-geodata.
The application can be accessed at jakobmiksch.github.io/random-geodata/.
First Try
The tool is designed as a web application so that it can be used with a web browser at any time without installation. I chose Vue.js as the front-end web framework and OpenLayers for the map rendering, as I already have a lot of experience with both.
It was important to me that the user can manually select the desired area on a map. This is easily possible with the on-board tools of OpenLayers. In the BBOX selected by the user, 100 random points are then generated. OpenLayers can export these as GeoJSON.
However, as I also wanted to enable other output formats such as Shapefile or GeoPackage, I started looking for ways to realize this. One possibility would be to run the conversion via a backend. But since I wanted to keep the whole application simple, I tried to solve the conversion via JavaScript libraries in the frontend. Unfortunately, I only found the library shp-write for the shapefile. The generation of GeoPackage also seems to be possible, but I found it relatively complicated.
However, shp-write also required a few tricks to get it to work. The following is my code (see also here) that I used for this:
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" });
More Formats and Coordinate Reference Systems
So far I have been able to export in GeoJSON and Shapefile, but I wanted more formats. Among other things to show an alternative to Shapefile (see here). I could only output WGS84 as the coordinate system. With OpenLayers, WebMercator would also have been possible. However, this CRS should really only be used for displaying and not for processing geodata.
All these requirements could certainly have been implemented with other JavaScript libraries, but I came across the project gdal3.js by chance. This provides the GDAL/OGR command line tools as WebAssembly (WASM) in the web browser. This means that a large part of the functionality offered by the native GDAL/OGR can be used on the client side in the browser.
Accessing GDAL/OGR in the browser also works via the command line interface of the various GDAL/OGR tools. In my case ogr2ogr. You pass a string array as an argument and get back a blob with the corresponding format.
Specifically, I used it to generate various formats such as GeoPackage, Postgres Dump, FlatGeoBuf and GeoJSON. However, I had problems with the shapefile export, as several sidecar files are automatically generated. For the coordinate systems, I concentrated on WGS84 and UTM for the time being.
Since the UTM coordinate system depends on the respective location on earth, I have written a function that derives the corresponding EPSG code from the coordinate system of the current location.
// 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)
}
The result is an easy-to-use website that can be used to generate random geodata. I have some more ideas on how to improve the application. These are noted as Issues in the repository. Contributors are welcome.
This post was originally written in German. It was translated with deepl.com/translator and manually revised by the author.