Geomap: Add GeoJSON gazetteer (#40589)

Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
This commit is contained in:
nikki-kigaandRyan McKinley authored and GitHub committed 2021-10-19 21:05:55 -07:00
1 parent 43878ff05f
commit 2eb88e1d1b
7 files changed
+189 -5

No files matched your search

@@ -7,7 +7,7 @@ import { css } from '@emotion/css';
const paths: Array<SelectableValue<string>> = [
{
label: 'Countries',
description: 'Lookup countries by name, two letter code, or three leter code',
description: 'Lookup countries by name, two letter code, or three letter code',
value: COUNTRIES_GAZETTEER_PATH,
},
{
@@ -15,6 +15,11 @@ const paths: Array<SelectableValue<string>> = [
description: 'Lookup states by name or 2 ',
value: 'public/gazetteer/usa-states.json',
},
{
label: 'Airports',
description: 'Lookup airports by id or code',
value: 'public/gazetteer/airports.geojson',
},
];
export const GazetteerPathEditor: FC<StandardEditorProps<string, any, any>> = ({ value, onChange, context }) => {
@@ -60,8 +65,8 @@ export const GazetteerPathEditor: FC<StandardEditorProps<string, any, any>> = ({
<b>({gaz.count})</b>
{gaz.examples(10).map((k) => (
<span key={k}>{k},</span>
))}{' '}
&ellipsis;
))}
{gaz.count > 10 && ' ...'}
</div>
)}
</>
@@ -1,6 +1,7 @@
import { KeyValue } from '@grafana/data';
import { getBackendSrv } from '@grafana/runtime';
import { loadWorldmapPoints } from './worldmap';
import { loadFromGeoJSON } from './geojson';
// http://geojson.xyz/
@@ -27,6 +28,12 @@ export function loadGazetteer(path: string, data: any): Gazetteer {
}
}
// try loading geojson
const features = data?.features;
if (Array.isArray(features) && data?.type === 'FeatureCollection') {
return loadFromGeoJSON(path, data);
}
return {
path,
error: 'Unable to parse locations',
@@ -0,0 +1,96 @@
import { getGazetteer } from './gazetteer';
let backendResults: any = { hello: 'world' };
const geojsonObject = {
type: 'FeatureCollection',
features: [
{
id: 'A',
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [0, 0],
},
properties: {
hello: 'A',
},
},
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [1, 1],
},
properties: {
some_code: 'B',
hello: 'B',
},
},
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [2, 2],
},
properties: {
an_id: 'C',
hello: 'C',
},
},
],
};
jest.mock('@grafana/runtime', () => ({
...((jest.requireActual('@grafana/runtime') as unknown) as object),
getBackendSrv: () => ({
get: jest.fn().mockResolvedValue(backendResults),
}),
}));
describe('Placename lookup from geojson format', () => {
beforeEach(() => {
backendResults = { hello: 'world' };
});
it('can lookup by id', async () => {
backendResults = geojsonObject;
const gaz = await getGazetteer('local');
expect(gaz.error).toBeUndefined();
expect(gaz.find('A')).toMatchInlineSnapshot(`
Object {
"coords": Array [
0,
0,
],
}
`);
});
it('can look up by a code', async () => {
backendResults = geojsonObject;
const gaz = await getGazetteer('airports');
expect(gaz.error).toBeUndefined();
expect(gaz.find('B')).toMatchInlineSnapshot(`
Object {
"coords": Array [
1,
1,
],
}
`);
});
it('can look up by an id property', async () => {
backendResults = geojsonObject;
const gaz = await getGazetteer('airports');
expect(gaz.error).toBeUndefined();
expect(gaz.find('C')).toMatchInlineSnapshot(`
Object {
"coords": Array [
2,
2,
],
}
`);
});
});
@@ -0,0 +1,75 @@
import GeoJSON from 'ol/format/GeoJSON';
import { PlacenameInfo, Gazetteer } from './gazetteer';
export interface GeoJSONPoint {
key?: string;
keys?: string[]; // new in grafana 8.1+
latitude: number;
longitude: number;
name?: string;
}
export function loadFromGeoJSON(path: string, body: any): Gazetteer {
const data = new GeoJSON().readFeatures(body);
let count = 0;
const values = new Map<string, PlacenameInfo>();
for (const f of data) {
const coords = f.getGeometry().getFlatCoordinates(); //for now point, eventually geometry
const info: PlacenameInfo = {
coords: coords,
};
const id = f.getId();
if (id) {
if (typeof id === 'number') {
values.set(id.toString(), info);
} else {
values.set(id, info);
values.set(id.toUpperCase(), info);
}
}
const properties = f.getProperties();
if (properties) {
for (const k of Object.keys(properties)) {
if (k.includes('_code') || k.includes('_id')) {
const value = properties[k];
if (value) {
if (typeof value === 'number') {
values.set(value.toString(), info);
} else {
values.set(value, info);
values.set(value.toUpperCase(), info);
}
}
}
}
}
count++;
}
return {
path,
find: (k) => {
let v = values.get(k);
if (!v && typeof k === 'string') {
v = values.get(k.toUpperCase());
}
return v;
},
count,
examples: (count) => {
const first: string[] = [];
if (values.size < 1) {
first.push('no values found');
} else {
for (const key of values.keys()) {
first.push(key);
if (first.length >= count) {
break;
}
}
}
return first;
},
};
}
@@ -10,7 +10,7 @@ jest.mock('@grafana/runtime', () => ({
}),
}));
describe('Placename lookups', () => {
describe('Placename lookup from worldmap format', () => {
beforeEach(() => {
backendResults = { hello: 'world' };
});
@@ -37,7 +37,7 @@ export function loadWorldmapPoints(path: string, data: WorldmapPoint[]): Gazette
path,
find: (k) => {
let v = values.get(k);
if (!v) {
if (!v && typeof k === 'string') {
v = values.get(k.toUpperCase());
}
return v;
+1
View File
@@ -0,0 +1 @@
{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Point","coordinates":[39.797222,47.115858]},"properties":{"type":"mid","name":"Rostov S.W.","abbrev":null,"location":"runway","gps_code":null,"iata_code":null,"wikipedia":null,"natlscale":10}},{"type":"Feature","geometry":{"type":"Point","coordinates":[21.628197,41.956455]},"properties":{"type":"mid","name":"Skopje","abbrev":"SKP","location":"terminal","gps_code":"LWSK","iata_code":"SKP","wikipedia":"http://en.wikipedia.org/wiki/Skopje_Airport","natlscale":10}},{"type":"Feature","geometry":{"type":"Point","coordinates":[2.3838,6.358247]},"properties":{"type":"mid","name":"Cotonou Cadjehon","abbrev":"COO","location":"terminal","gps_code":"DBBB","iata_code":"COO","wikipedia":"http://en.wikipedia.org/wiki/Cadjehoun_Airport","natlscale":50}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-130.445587,54.292]},"properties":{"type":"mid","name":"Prince Rupert","abbrev":"YPR","location":"terminal","gps_code":"CYPR","iata_code":"YPR","wikipedia":"http://en.wikipedia.org/wiki/Prince_Rupert_Airport","natlscale":50}},{"type":"Feature","geometry":{"type":"Point","coordinates":[31.016249,27.050816]},"properties":{"type":"mid","name":"Asyut","abbrev":"ATZ","location":"terminal","gps_code":"HEAT","iata_code":"ATZ","wikipedia":"http://en.wikipedia.org/wiki/Assiut_Airport","natlscale":20}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-165.441642,64.507221]},"properties":{"type":"mid","name":"Nome","abbrev":"OME","location":"terminal","gps_code":"PAOM","iata_code":"OME","wikipedia":"http://en.wikipedia.org/wiki/Nome_Airport","natlscale":50}},{"type":"Feature","geometry":{"type":"Point","coordinates":[48.747107,31.343159]},"properties":{"type":"mid","name":"Ahwaz","abbrev":"AWZ","location":"terminal","gps_code":"OIAW","iata_code":"AWZ","wikipedia":"http://en.wikipedia.org/wiki/Ahwaz_Airport","natlscale":8}},{"type":"Feature","geometry":{"type":"Point","coordinates":[75.758483,31.432942]},"properties":{"type":"military mid","name":"Adampur","abbrev":"VIAX","location":"runway","gps_code":"VIAX","iata_code":null,"wikipedia":null,"natlscale":8}},{"type":"Feature","geometry":{"type":"Point","coordinates":[83.223522,17.727958]},"properties":{"type":"mid","name":"Vishakapatnam","abbrev":"VTZ","location":"terminal","gps_code":"VEVZ","iata_code":"VTZ","wikipedia":"http://en.wikipedia.org/wiki/Visakhapatnam_Airport","natlscale":10}},{"type":"Feature","geometry":{"type":"Point","coordinates":[77.038894,11.030142]},"properties":{"type":"mid","name":"Coimbatore","abbrev":"CJB","location":"terminal","gps_code":"VOCB","iata_code":"CJB","wikipedia":"http://en.wikipedia.org/wiki/Coimbatore_Airport","natlscale":10}},{"type":"Feature","geometry":{"type":"Point","coordinates":[114.246947,30.601714]},"properties":{"type":"military mid","name":"Wang-Chia Tun Airbase","abbrev":"ZHHH","location":"runway","gps_code":null,"iata_code":null,"wikipedia":null,"natlscale":20}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-49.173709,-25.536]},"properties":{"type":"mid","name":"Afonso Pena Int'l","abbrev":"CWB","location":"terminal","gps_code":"SBCT","iata_code":"CWB","wikipedia":"http://en.wikipedia.org/wiki/Afonso_Pena_International_Airport","natlscale":50}},{"type":"Feature","geometry":{"type":"Point","coordinates":[81.740378,21.185987]},"properties":{"type":"mid","name":"Raipur","abbrev":"RPR","location":"terminal","gps_code":"VARP","iata_code":"RPR","wikipedia":"http://en.wikipedia.org/wiki/Raipur_Airport","natlscale":20}},{"type":"Feature","geometry":{"type":"Point","coordinates":[67.152128,24.898524]},"properties":{"type":"mid","name":"Karachi Civil","abbrev":"KHI","location":"terminal","gps_code":"OPKC","iata_code":"KHI","wikipedia":"http://en.wikipedia.org/wiki/Jinnah_International_Airport","natlscale":50}},{"type":"Feature","geometry":{"type":"Point","coordinates":[8.562213,47.45239]},"properties":{"type":"major","name":"Zurich Int'l","abbrev":"ZRH","location":"terminal","gps_code":"LSZH","iata_code":"ZRH","wikipedia":"http://en.wikipedia.org/wiki/Z%C3%BCrich_Airport","natlscale":75}},{"type":"Feature","geometry":{"type":"Point","coordinates":[75.957072,30.85036]},"properties":{"type":"small","name":"Sahnewal","abbrev":"LUH","location":"terminal","gps_code":"VILD","iata_code":"LUH","wikipedia":"http://en.wikipedia.org/wiki/Sahnewal_Airport","natlscale":8}},{"type":"Feature","geometry":{"type":"Point","coordinates":[75.93306,17.625415]},"properties":{"type":"mid","name":"Solapur","abbrev":"SSE","location":"terminal","gps_code":"VASL","iata_code":"SSE","wikipedia":"http://en.wikipedia.org/wiki/Solapur_Airport","natlscale":8}},{"type":"Feature","geometry":{"type":"Point","coordinates":[85.323597,23.317725]},"properties":{"type":"mid","name":"Birsa Munda","abbrev":"IXR","location":"terminal","gps_code":"VERC","iata_code":"IXR","wikipedia":"http://en.wikipedia.org/wiki/Birsa_Munda_Airport","natlscale":8}},{"type":"Feature","geometry":Line truncated