[Maps] Implement fields and bounds retrieval on GeoJsonFileSource (#88294)

This commit is contained in:
Thomas Neirynck 2021-01-26 12:32:12 -05:00 committed by GitHub
parent 1f644e44c9
commit 0076384a0f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 240 additions and 23 deletions

View file

@ -156,7 +156,13 @@ export type TiledSingleLayerVectorSourceDescriptor = AbstractSourceDescriptor &
tooltipProperties: string[];
};
export type GeoJsonFileFieldDescriptor = {
name: string;
type: 'string' | 'number';
};
export type GeojsonFileSourceDescriptor = {
__fields?: GeoJsonFileFieldDescriptor[];
__featureCollection: FeatureCollection;
name: string;
type: string;

View file

@ -0,0 +1,43 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { FIELD_ORIGIN } from '../../../common/constants';
import { IField, AbstractField } from './field';
import { IVectorSource } from '../sources/vector_source';
import { GeoJsonFileSource } from '../sources/geojson_file_source';
export class GeoJsonFileField extends AbstractField implements IField {
private readonly _source: GeoJsonFileSource;
private readonly _dataType: string;
constructor({
fieldName,
source,
origin,
dataType,
}: {
fieldName: string;
source: GeoJsonFileSource;
origin: FIELD_ORIGIN;
dataType: string;
}) {
super({ fieldName, origin });
this._source = source;
this._dataType = dataType;
}
getSource(): IVectorSource {
return this._source;
}
async getLabel(): Promise<string> {
return this.getName();
}
async getDataType(): Promise<string> {
return this._dataType;
}
}

View file

@ -14,7 +14,7 @@ import {
SCALING_TYPES,
} from '../../../../common/constants';
import { getFileUploadComponent } from '../../../kibana_services';
import { GeojsonFileSource } from '../../sources/geojson_file_source';
import { GeoJsonFileSource } from '../../sources/geojson_file_source';
import { VectorLayer } from '../../layers/vector_layer/vector_layer';
import { createDefaultLayerDescriptor } from '../../sources/es_search_source';
import { RenderWizardArguments } from '../../layers/layer_wizard_registry';
@ -79,7 +79,10 @@ export class ClientFileCreateSourceEditor extends Component<RenderWizardArgument
return;
}
const sourceDescriptor = GeojsonFileSource.createDescriptor(geojsonFile, name);
const sourceDescriptor = GeoJsonFileSource.createDescriptor({
__featureCollection: geojsonFile,
name,
});
const layerDescriptor = VectorLayer.createDescriptor(
{ sourceDescriptor },
this.props.mapColors

View file

@ -0,0 +1,105 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { GeoJsonFileSource } from './geojson_file_source';
import { BoundsFilters } from '../vector_source';
import { FIELD_ORIGIN } from '../../../../common/constants';
describe('GeoJsonFileSource', () => {
describe('getName', () => {
it('should get default display name', async () => {
const geojsonFileSource = new GeoJsonFileSource({});
expect(await geojsonFileSource.getDisplayName()).toBe('Features');
});
});
describe('getBounds', () => {
it('should get null bounds', async () => {
const geojsonFileSource = new GeoJsonFileSource({});
expect(
await geojsonFileSource.getBoundsForFilters(({} as unknown) as BoundsFilters, () => {})
).toEqual(null);
});
it('should get bounds from feature collection', async () => {
const geojsonFileSource = new GeoJsonFileSource({
__featureCollection: {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [0, 1],
},
properties: {},
},
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [2, 3],
},
properties: {},
},
],
},
});
expect(geojsonFileSource.isBoundsAware()).toBe(true);
expect(
await geojsonFileSource.getBoundsForFilters(({} as unknown) as BoundsFilters, () => {})
).toEqual({
maxLat: 3,
maxLon: 2,
minLat: 1,
minLon: 0,
});
});
});
describe('getFields', () => {
it('should get fields from config', async () => {
const geojsonFileSource = new GeoJsonFileSource({
__fields: [
{
type: 'string',
name: 'foo',
},
{
type: 'number',
name: 'bar',
},
],
});
const fields = await geojsonFileSource.getFields();
const actualFields = fields.map(async (field) => {
return {
dataType: await field.getDataType(),
origin: field.getOrigin(),
name: field.getName(),
source: field.getSource(),
};
});
expect(await Promise.all(actualFields)).toEqual([
{
dataType: 'string',
origin: FIELD_ORIGIN.SOURCE,
source: geojsonFileSource,
name: 'foo',
},
{
dataType: 'number',
origin: FIELD_ORIGIN.SOURCE,
source: geojsonFileSource,
name: 'bar',
},
]);
});
});
});

View file

@ -5,13 +5,22 @@
*/
import { Feature, FeatureCollection } from 'geojson';
import { AbstractVectorSource, GeoJsonWithMeta } from '../vector_source';
import { EMPTY_FEATURE_COLLECTION, SOURCE_TYPES } from '../../../../common/constants';
import { GeojsonFileSourceDescriptor } from '../../../../common/descriptor_types';
import { AbstractVectorSource, BoundsFilters, GeoJsonWithMeta } from '../vector_source';
import { EMPTY_FEATURE_COLLECTION, FIELD_ORIGIN, SOURCE_TYPES } from '../../../../common/constants';
import {
GeoJsonFileFieldDescriptor,
GeojsonFileSourceDescriptor,
MapExtent,
} from '../../../../common/descriptor_types';
import { registerSource } from '../source_registry';
import { IField } from '../../fields/field';
import { getFeatureCollectionBounds } from '../../util/get_feature_collection_bounds';
import { GeoJsonFileField } from '../../fields/geojson_file_field';
import { Adapters } from '../../../../../../../src/plugins/inspector/common/adapters';
function getFeatureCollection(geoJson: Feature | FeatureCollection | null): FeatureCollection {
function getFeatureCollection(
geoJson: Feature | FeatureCollection | null | undefined
): FeatureCollection {
if (!geoJson) {
return EMPTY_FEATURE_COLLECTION;
}
@ -30,18 +39,73 @@ function getFeatureCollection(geoJson: Feature | FeatureCollection | null): Feat
return EMPTY_FEATURE_COLLECTION;
}
export class GeojsonFileSource extends AbstractVectorSource {
export class GeoJsonFileSource extends AbstractVectorSource {
static createDescriptor(
geoJson: Feature | FeatureCollection | null,
name: string
descriptor: Partial<GeojsonFileSourceDescriptor>
): GeojsonFileSourceDescriptor {
return {
type: SOURCE_TYPES.GEOJSON_FILE,
__featureCollection: getFeatureCollection(geoJson),
name,
__featureCollection: getFeatureCollection(descriptor.__featureCollection),
__fields: descriptor.__fields || [],
name: descriptor.name || 'Features',
};
}
constructor(descriptor: Partial<GeojsonFileSourceDescriptor>, inspectorAdapters?: Adapters) {
const normalizedDescriptor = GeoJsonFileSource.createDescriptor(descriptor);
super(normalizedDescriptor, inspectorAdapters);
}
_getFields(): GeoJsonFileFieldDescriptor[] {
const fields = (this._descriptor as GeojsonFileSourceDescriptor).__fields;
return fields ? fields : [];
}
createField({ fieldName }: { fieldName: string }): IField {
const fields = this._getFields();
const descriptor: GeoJsonFileFieldDescriptor | undefined = fields.find((field) => {
return field.name === fieldName;
});
if (!descriptor) {
throw new Error(
`Cannot find corresponding field ${fieldName} in __fields array ${JSON.stringify(
this._getFields()
)} `
);
}
return new GeoJsonFileField({
fieldName: descriptor.name,
source: this,
origin: FIELD_ORIGIN.SOURCE,
dataType: descriptor.type,
});
}
async getFields(): Promise<IField[]> {
const fields = this._getFields();
return fields.map((field: GeoJsonFileFieldDescriptor) => {
return new GeoJsonFileField({
fieldName: field.name,
source: this,
origin: FIELD_ORIGIN.SOURCE,
dataType: field.type,
});
});
}
isBoundsAware(): boolean {
return true;
}
async getBoundsForFilters(
boundsFilters: BoundsFilters,
registerCancelCallback: (callback: () => void) => void
): Promise<MapExtent | null> {
const featureCollection = (this._descriptor as GeojsonFileSourceDescriptor).__featureCollection;
return getFeatureCollectionBounds(featureCollection, false);
}
async getGeoJsonWithMeta(): Promise<GeoJsonWithMeta> {
return {
data: (this._descriptor as GeojsonFileSourceDescriptor).__featureCollection,
@ -49,10 +113,6 @@ export class GeojsonFileSource extends AbstractVectorSource {
};
}
createField({ fieldName }: { fieldName: string }): IField {
throw new Error('Not implemented');
}
async getDisplayName() {
return (this._descriptor as GeojsonFileSourceDescriptor).name;
}
@ -63,6 +123,6 @@ export class GeojsonFileSource extends AbstractVectorSource {
}
registerSource({
ConstructorFunction: GeojsonFileSource,
ConstructorFunction: GeoJsonFileSource,
type: SOURCE_TYPES.GEOJSON_FILE,
});

View file

@ -4,4 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
export { GeojsonFileSource } from './geojson_file_source';
export { GeoJsonFileSource } from './geojson_file_source';

View file

@ -22,7 +22,7 @@ import { TiledVectorLayer } from '../classes/layers/tiled_vector_layer/tiled_vec
import { copyPersistentState, TRACKED_LAYER_DESCRIPTOR } from '../reducers/util';
import { InnerJoin } from '../classes/joins/inner_join';
import { getSourceByType } from '../classes/sources/source_registry';
import { GeojsonFileSource } from '../classes/sources/geojson_file_source';
import { GeoJsonFileSource } from '../classes/sources/geojson_file_source';
import {
SOURCE_DATA_REQUEST_ID,
STYLE_TYPE,
@ -241,10 +241,10 @@ export const getSpatialFiltersLayer = createSelector(
type: 'FeatureCollection',
features: extractFeaturesFromFilters(filters),
};
const geoJsonSourceDescriptor = GeojsonFileSource.createDescriptor(
featureCollection,
'spatialFilters'
);
const geoJsonSourceDescriptor = GeoJsonFileSource.createDescriptor({
__featureCollection: featureCollection,
name: 'spatialFilters',
});
return new VectorLayer({
layerDescriptor: VectorLayer.createDescriptor({
@ -272,7 +272,7 @@ export const getSpatialFiltersLayer = createSelector(
},
}),
}),
source: new GeojsonFileSource(geoJsonSourceDescriptor),
source: new GeoJsonFileSource(geoJsonSourceDescriptor),
});
}
);