[Maps] Support GeometryCollections in GeoJson upload (#93507)

This commit is contained in:
Nathan Reese 2021-03-04 11:01:33 -07:00 committed by GitHub
parent 667cb14f5a
commit 05db7c60b1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 269 additions and 66 deletions

View file

@ -25,6 +25,29 @@ const FEATURE_COLLECTION = {
],
};
const GEOMETRY_COLLECTION_FEATURE = {
type: 'Feature',
properties: {
population: 200,
},
geometry: {
type: 'GeometryCollection',
geometries: [
{
type: 'Point',
coordinates: [100.0, 0.0],
},
{
type: 'LineString',
coordinates: [
[101.0, 0.0],
[102.0, 1.0],
],
},
],
},
};
describe('previewFile', () => {
const FILE_WITH_FEATURE_COLLECTION = new File(
[JSON.stringify(FEATURE_COLLECTION)],
@ -60,6 +83,27 @@ describe('previewFile', () => {
});
});
test('should read GeometryCollection feature', async () => {
const fileWithGeometryCollectionFeature = new File(
[
JSON.stringify({
type: 'FeatureCollection',
features: [GEOMETRY_COLLECTION_FEATURE],
}),
],
'testfile.json',
{ type: 'text/json' }
);
const importer = new GeoJsonImporter(fileWithGeometryCollectionFeature);
const results = await importer.previewFile();
expect(results).toEqual({
previewCoverage: 100,
hasPoints: false,
hasShapes: true,
features: [GEOMETRY_COLLECTION_FEATURE],
});
});
test('should remove features without geometry', async () => {
const fileWithFeaturesWithoutGeometry = new File(
[
@ -193,11 +237,36 @@ describe('toEsDocs', () => {
expect(esDocs).toEqual([
{
coordinates: {
type: 'point',
type: 'Point',
coordinates: [-112.0372, 46.608058],
},
population: 200,
},
]);
});
test('should convert GeometryCollection feature to geo_shape ES documents', () => {
const esDocs = toEsDocs([GEOMETRY_COLLECTION_FEATURE], ES_FIELD_TYPES.GEO_SHAPE);
expect(esDocs).toEqual([
{
coordinates: {
type: 'GeometryCollection',
geometries: [
{
type: 'Point',
coordinates: [100.0, 0.0],
},
{
type: 'LineString',
coordinates: [
[101.0, 0.0],
[102.0, 1.0],
],
},
],
},
population: 200,
},
]);
});
});

View file

@ -5,15 +5,7 @@
* 2.0.
*/
import {
Feature,
Point,
MultiPoint,
LineString,
MultiLineString,
Polygon,
MultiPolygon,
} from 'geojson';
import { Feature, Point } from 'geojson';
import { i18n } from '@kbn/i18n';
// @ts-expect-error
import { JSONLoader, loadInBatches } from './loaders';
@ -71,7 +63,8 @@ export class GeoJsonImporter extends Importer {
this._geometryTypesMap.has('LineString') ||
this._geometryTypesMap.has('MultiLineString') ||
this._geometryTypesMap.has('Polygon') ||
this._geometryTypesMap.has('MultiPolygon'),
this._geometryTypesMap.has('MultiPolygon') ||
this._geometryTypesMap.has('GeometryCollection'),
};
}
@ -266,23 +259,12 @@ export function toEsDocs(
const esDocs = [];
for (let i = 0; i < features.length; i++) {
const feature = features[i];
const geometry = feature.geometry as
| Point
| MultiPoint
| LineString
| MultiLineString
| Polygon
| MultiPolygon;
const coordinates =
geoFieldType === ES_FIELD_TYPES.GEO_SHAPE
? {
type: geometry.type.toLowerCase(),
coordinates: geometry.coordinates,
}
: geometry.coordinates;
const properties = feature.properties ? feature.properties : {};
esDocs.push({
coordinates,
coordinates:
geoFieldType === ES_FIELD_TYPES.GEO_SHAPE
? feature.geometry
: (feature.geometry as Point).coordinates,
...properties,
});
}

View file

@ -281,3 +281,130 @@ test('should create centroid feature for multi polygon', () => {
},
});
});
test('should create centroid feature for GeometryCollection with Point', () => {
const featureCollection: FeatureCollection = {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'GeometryCollection',
geometries: [
{
type: 'Point',
coordinates: [100.0, 0.0],
},
],
},
properties: {
prop0: 'value0',
prop1: 0.0,
},
},
],
};
const centroidFeatures = getCentroidFeatures(featureCollection);
expect(centroidFeatures.length).toBe(1);
expect(centroidFeatures[0]).toEqual({
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [100.0, 0.0],
},
properties: {
__kbn_is_centroid_feature__: true,
prop0: 'value0',
prop1: 0.0,
},
});
});
test('should create centroid feature for GeometryCollection with MultiPoint', () => {
const featureCollection: FeatureCollection = {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'GeometryCollection',
geometries: [
{
type: 'MultiPoint',
coordinates: [
[10, 40],
[40, 30],
[20, 20],
[30, 10],
],
},
],
},
properties: {
prop0: 'value0',
prop1: 0.0,
},
},
],
};
const centroidFeatures = getCentroidFeatures(featureCollection);
expect(centroidFeatures.length).toBe(1);
expect(centroidFeatures[0]).toEqual({
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [10, 40],
},
properties: {
__kbn_is_centroid_feature__: true,
prop0: 'value0',
prop1: 0.0,
},
});
});
test('should create centroid feature for GeometryCollection with Polygon', () => {
const featureCollection: FeatureCollection = {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'GeometryCollection',
geometries: [
{
type: 'Polygon',
coordinates: [
[
[35, 10],
[45, 45],
[15, 40],
[10, 20],
[35, 10],
],
],
},
],
},
properties: {
prop0: 'value0',
prop1: 0.0,
},
},
],
};
const centroidFeatures = getCentroidFeatures(featureCollection);
expect(centroidFeatures.length).toBe(1);
expect(centroidFeatures[0]).toEqual({
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [27.526881720430108, 28.70967741935484],
},
properties: {
__kbn_is_centroid_feature__: true,
prop0: 'value0',
prop1: 0.0,
},
});
});

View file

@ -9,8 +9,10 @@ import {
Feature,
FeatureCollection,
Geometry,
GeometryCollection,
LineString,
MultiLineString,
MultiPoint,
MultiPolygon,
} from 'geojson';
import turfAlong from '@turf/along';
@ -26,7 +28,7 @@ import {
} from './constants';
export function getCentroidFeatures(featureCollection: FeatureCollection): Feature[] {
const centroidFeatures = [];
const centroids = [];
for (let i = 0; i < featureCollection.features.length; i++) {
const feature = featureCollection.features[i];
@ -35,43 +37,68 @@ export function getCentroidFeatures(featureCollection: FeatureCollection): Featu
continue;
}
let centroidGeometry: Geometry | null = null;
if (feature.geometry.type === GEO_JSON_TYPE.LINE_STRING) {
centroidGeometry = getLineCentroid(feature);
} else if (feature.geometry.type === GEO_JSON_TYPE.MULTI_LINE_STRING) {
const coordinates = (feature.geometry as MultiLineString).coordinates;
let longestLine = coordinates[0];
let longestLength = turfLength(lineString(longestLine));
for (let j = 1; j < coordinates.length; j++) {
const nextLine = coordinates[j];
const nextLength = turfLength(lineString(nextLine));
if (nextLength > longestLength) {
longestLine = nextLine;
longestLength = nextLength;
}
}
centroidGeometry = getLineCentroid(lineString(longestLine) as Feature);
} else if (feature.geometry.type === GEO_JSON_TYPE.POLYGON) {
centroidGeometry = turfCenterOfMass(feature).geometry;
} else if (feature.geometry.type === GEO_JSON_TYPE.MULTI_POLYGON) {
const coordinates = (feature.geometry as MultiPolygon).coordinates;
let largestPolygon = coordinates[0];
let largestArea = turfArea(polygon(largestPolygon));
for (let j = 1; j < coordinates.length; j++) {
const nextPolygon = coordinates[j];
const nextArea = turfArea(polygon(nextPolygon));
if (nextArea > largestArea) {
largestPolygon = nextPolygon;
largestArea = nextArea;
}
}
centroidGeometry = turfCenterOfMass(polygon(largestPolygon)).geometry;
} else if (feature.geometry.type === GEO_JSON_TYPE.GEOMETRY_COLLECTION) {
throw new Error('Should not have features with geometrycollection');
const centroid = getCentroid(feature);
if (centroid) {
centroids.push(centroid);
}
}
return centroids;
}
if (centroidGeometry) {
centroidFeatures.push({
export function getCentroid(feature: Feature): Feature | null {
let centroidGeometry: Geometry | null = null;
if (feature.geometry.type === GEO_JSON_TYPE.LINE_STRING) {
centroidGeometry = getLineCentroid(feature);
} else if (feature.geometry.type === GEO_JSON_TYPE.MULTI_LINE_STRING) {
const coordinates = (feature.geometry as MultiLineString).coordinates;
let longestLine = coordinates[0];
let longestLength = turfLength(lineString(longestLine));
for (let j = 1; j < coordinates.length; j++) {
const nextLine = coordinates[j];
const nextLength = turfLength(lineString(nextLine));
if (nextLength > longestLength) {
longestLine = nextLine;
longestLength = nextLength;
}
}
centroidGeometry = getLineCentroid(lineString(longestLine) as Feature);
} else if (feature.geometry.type === GEO_JSON_TYPE.POLYGON) {
centroidGeometry = turfCenterOfMass(feature).geometry;
} else if (feature.geometry.type === GEO_JSON_TYPE.MULTI_POLYGON) {
const coordinates = (feature.geometry as MultiPolygon).coordinates;
let largestPolygon = coordinates[0];
let largestArea = turfArea(polygon(largestPolygon));
for (let j = 1; j < coordinates.length; j++) {
const nextPolygon = coordinates[j];
const nextArea = turfArea(polygon(nextPolygon));
if (nextArea > largestArea) {
largestPolygon = nextPolygon;
largestArea = nextArea;
}
}
centroidGeometry = turfCenterOfMass(polygon(largestPolygon)).geometry;
} else if (
feature.geometry.type === GEO_JSON_TYPE.GEOMETRY_COLLECTION &&
(feature.geometry as GeometryCollection).geometries.length
) {
const firstGeometry = (feature.geometry as GeometryCollection).geometries[0];
if (firstGeometry.type === GEO_JSON_TYPE.POINT) {
centroidGeometry = firstGeometry;
} else if (firstGeometry.type === GEO_JSON_TYPE.MULTI_POINT) {
centroidGeometry = {
type: 'Point',
coordinates: (firstGeometry as MultiPoint).coordinates[0],
};
} else {
return getCentroid({
...feature,
geometry: firstGeometry,
});
}
}
return centroidGeometry
? ({
type: 'Feature',
id: feature.id,
properties: {
@ -79,10 +106,8 @@ export function getCentroidFeatures(featureCollection: FeatureCollection): Featu
[KBN_IS_CENTROID_FEATURE]: true,
},
geometry: centroidGeometry,
} as Feature);
}
}
return centroidFeatures;
} as Feature)
: null;
}
function getLineCentroid(feature: Feature): Geometry {