Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[7.x] [Maps] Implement fields and bounds retrieval on GeoJsonFileSource (#88294) #89331

Merged
merged 1 commit into from
Jan 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
43 changes: 43 additions & 0 deletions x-pack/plugins/maps/public/classes/fields/geojson_file_field.ts
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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',
},
]);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -30,29 +39,80 @@ 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,
meta: {},
};
}

createField({ fieldName }: { fieldName: string }): IField {
throw new Error('Not implemented');
}

async getDisplayName() {
return (this._descriptor as GeojsonFileSourceDescriptor).name;
}
Expand All @@ -63,6 +123,6 @@ export class GeojsonFileSource extends AbstractVectorSource {
}

registerSource({
ConstructorFunction: GeojsonFileSource,
ConstructorFunction: GeoJsonFileSource,
type: SOURCE_TYPES.GEOJSON_FILE,
});
Original file line number Diff line number Diff line change
Expand Up @@ -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';
12 changes: 6 additions & 6 deletions x-pack/plugins/maps/public/selectors/map_selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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({
Expand Down Expand Up @@ -272,7 +272,7 @@ export const getSpatialFiltersLayer = createSelector(
},
}),
}),
source: new GeojsonFileSource(geoJsonSourceDescriptor),
source: new GeoJsonFileSource(geoJsonSourceDescriptor),
});
}
);
Expand Down