[Maps] Support GeometryCollections in GeoJson upload (#93507)
This commit is contained in:
parent
667cb14f5a
commit
05db7c60b1
|
@ -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,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue