GIS
The geo editor edits map features and their attributes in the page: points, lines and polygons with holes, drawn with snapping to the vertices and edges nearby, then moved, reshaped, split along a line, merged, buffered by a number of metres and simplified. The attribute table under the map keeps in step with it both ways, with typed fields you add, rename and retype. Lengths and areas are geodesic, worked out on the WGS84 ellipsoid.
It reads and writes GeoJSON, Shapefiles (loose or zipped, with their .dbf table and .prj), KML and KMZ, GPX, and CSV with coordinate columns or WKT. Coordinates stay in the system they came in - longitude and latitude, Web Mercator, UTM, the British National Grid, the Irish grids and others a .prj describes - and convert to another when you ask. A topology check finds polygons that cross themselves, overlap or leave gaps between neighbours, and fixes them. The map is drawn over your own GeoJSON as background layers; no map tiles are fetched.
Try it with your own Shapefiles, GeoJSON and KML in confidence: they are opened by your browser, on your device. Nothing is uploaded, no copy reaches us or anyone else, and nothing is kept once you close this tab.
Field boundaries, drawn and checked
An invented farm, Marrowby Farm, placed out at sea so it cannot be mistaken for real land: five made-up fields with their crop, owner reference and recorded area, and a hedge, over a made-up background of lanes, a brook and a wood. Three drawing faults are hidden in it - two fields overlap, two leave a thin gap between them and one boundary crosses itself. Check the boundaries and fix them, then measure, merge, split and buffer.
// Node 18 or later, from the Gold package: check a farm's boundary files as they arrive, before they are accepted.
import { readFileSync, readdirSync, writeFileSync } from 'node:fs';
import { readGeoFiles, checkLayer, checkTopology, measureGeometry, writeGeoFile } from './dist/node/bmx-geo-editor.mjs';
const dir = 'incoming/marrowby-farm';
const files = readdirSync(dir).map(name => ({ name, data: readFileSync(`${dir}/${name}`) }));
const { layer, notes } = await readGeoFiles(files);
for (const note of notes) console.log('note:', note.message);
const problems = [...checkLayer(layer), ...checkTopology(layer, { gapLimit: 5000 })];
for (const p of problems) console.log(p.severity, p.message);
for (const f of layer.features) {
const ha = measureGeometry(f.geometry, layer.crs).area / 10000;
const stated = f.properties.Area_ha;
if (stated != null && Math.abs(ha - stated) > 0.05) console.log(`${f.properties.Field}: stated ${stated} ha, measures ${ha.toFixed(2)} ha`);
}
if (problems.some(p => p.severity === 'error')) process.exitCode = 1;
else {
const out = await writeGeoFile(layer, 'geojson');
writeFileSync(`accepted/${out.name}`, out.data);
}
A council's assets, on the British grid
A made-up council's street lighting on an invented seafront, Wrenmouth Esplanade: lighting columns, a feeder pillar and two cable runs, with eastings and northings on the British National Grid (EPSG:27700). The numbers are invented and lie out in the Channel, well away from any real street. Convert the layer to longitude and latitude and back, take out the extra vertices of a cable digitised by hand, and take the layer as CSV with WKT or as KML.