diff --git a/x-pack/plugins/file_upload/public/importer/geo/geojson_clean_and_validate.js b/x-pack/plugins/file_upload/public/importer/geo/geojson_clean_and_validate.js index 17b4f94b52d5f..e16374d851de8 100644 --- a/x-pack/plugins/file_upload/public/importer/geo/geojson_clean_and_validate.js +++ b/x-pack/plugins/file_upload/public/importer/geo/geojson_clean_and_validate.js @@ -8,6 +8,13 @@ import * as jsts from 'jsts'; import rewind from '@mapbox/geojson-rewind'; +// The GeoJSON specification suggests limiting coordinate precision to six decimal places +// See https://datatracker.ietf.org/doc/html/rfc7946#section-11.2 +// We can enforce rounding to six decimal places by setting the PrecisionModel scale +// scale = 10^n where n = maximum number of decimal places +const precisionModel = new jsts.geom.PrecisionModel(Math.pow(10, 6)); +const geometryPrecisionReducer = new jsts.precision.GeometryPrecisionReducer(precisionModel); +geometryPrecisionReducer.setChangePrecisionModel(true); const geoJSONReader = new jsts.io.GeoJSONReader(); const geoJSONWriter = new jsts.io.GeoJSONWriter(); @@ -36,6 +43,8 @@ export function cleanGeometry({ geometry }) { if (!geometry) { return null; } - const geometryToWrite = geometry.isSimple() || geometry.isValid() ? geometry : geometry.buffer(0); + + // GeometryPrecisionReducer will automatically clean invalid geometries + const geometryToWrite = geometryPrecisionReducer.reduce(geometry); return geoJSONWriter.write(geometryToWrite); } diff --git a/x-pack/plugins/file_upload/public/importer/geo/geojson_clean_and_validate.test.js b/x-pack/plugins/file_upload/public/importer/geo/geojson_clean_and_validate.test.js index 0f8d126251dfb..8c9000e66e811 100644 --- a/x-pack/plugins/file_upload/public/importer/geo/geojson_clean_and_validate.test.js +++ b/x-pack/plugins/file_upload/public/importer/geo/geojson_clean_and_validate.test.js @@ -102,6 +102,44 @@ describe('geo_json_clean_and_validate', () => { }); }); + it('should reduce coordinate precision', () => { + const ludicrousPrecisionGeoJson = { + type: 'Feature', + properties: {}, + geometry: { + type: 'Polygon', + coordinates: [ + [ + [108.28125, 61.77312286453146], + [72.0703125, 46.31658418182218], + [99.49218749999999, 22.917922936146045], + [133.2421875, 27.059125784374068], + [139.5703125, 52.908902047770255], + [108.28125, 61.77312286453146], + ], + ], + }, + }; + + expect(geoJsonCleanAndValidate(ludicrousPrecisionGeoJson)).toEqual({ + type: 'Feature', + properties: {}, + geometry: { + type: 'Polygon', + coordinates: [ + [ + [108.28125, 61.773123], + [72.070313, 46.316584], + [99.492187, 22.917923], + [133.242188, 27.059126], + [139.570313, 52.908902], + [108.28125, 61.773123], + ], + ], + }, + }); + }); + it('should reverse counter-clockwise winding order', () => { const counterClockwiseGeoJson = { type: 'Feature',