From b820d8f7086092629d91a033a68d723da77d980b Mon Sep 17 00:00:00 2001 From: Clay Anderson Date: Wed, 23 Sep 2020 21:12:09 -0600 Subject: [PATCH 1/6] Don't drop feature id when importing --- modules/editor/src/lib/importer.ts | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/modules/editor/src/lib/importer.ts b/modules/editor/src/lib/importer.ts index 68dee2269..93e071d0f 100644 --- a/modules/editor/src/lib/importer.ts +++ b/modules/editor/src/lib/importer.ts @@ -53,33 +53,29 @@ function getCleanedFeatures(geojson: AnyGeoJson): Feature[] { } function getCleanedFeature(feature: Feature): Feature { - let geometry = feature.geometry; + const { id } = feature; // reduce null-checking const properties = feature.properties || {}; - // @ts-ignore + + let geometry = feature.geometry; if (geometry.type === 'GeometryCollection' && geometry.geometries.length === 1) { // There's only one geometry - // @ts-ignore geometry = geometry.geometries[0]; - // @ts-ignore } else if (geometry.type === 'GeometryCollection' && geometry.geometries.length > 1) { - // @ts-ignore const types = new Set(geometry.geometries.map((g) => g.type)); if (types.size === 1) { // See if it can be combined into a Multi* geometry const type = types.values().next().value; if (type === 'Polygon') { - // Combine all the polygons into a single MultiPolygon + // Combine all the Polygons into a single MultiPolygon geometry = { type: 'MultiPolygon', - // @ts-ignore coordinates: geometry.geometries.map((g) => g.coordinates), }; } else if (type === 'LineString') { - // Combine all the polygons into a single MultiPolygon + // Combine all the LineStrings into a single MultiLineString geometry = { type: 'MultiLineString', - // @ts-ignore coordinates: geometry.geometries.map((g) => g.coordinates), }; } @@ -88,9 +84,10 @@ function getCleanedFeature(feature: Feature): Feature { throw Error('GeometryCollection geometry type not yet supported'); } } - // @ts-ignore + return { type: 'Feature', + id, geometry, properties, }; From 70af0d69f399e4f685934b68429908ba96784837 Mon Sep 17 00:00:00 2001 From: Clay Anderson Date: Wed, 23 Sep 2020 22:04:54 -0600 Subject: [PATCH 2/6] ts-ignore for now --- modules/editor/src/lib/importer.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/modules/editor/src/lib/importer.ts b/modules/editor/src/lib/importer.ts index 93e071d0f..b7dc95a25 100644 --- a/modules/editor/src/lib/importer.ts +++ b/modules/editor/src/lib/importer.ts @@ -58,10 +58,14 @@ function getCleanedFeature(feature: Feature): Feature { const properties = feature.properties || {}; let geometry = feature.geometry; + // @ts-ignore if (geometry.type === 'GeometryCollection' && geometry.geometries.length === 1) { // There's only one geometry + // @ts-ignore geometry = geometry.geometries[0]; + // @ts-ignore } else if (geometry.type === 'GeometryCollection' && geometry.geometries.length > 1) { + // @ts-ignore const types = new Set(geometry.geometries.map((g) => g.type)); if (types.size === 1) { // See if it can be combined into a Multi* geometry @@ -70,12 +74,14 @@ function getCleanedFeature(feature: Feature): Feature { // Combine all the Polygons into a single MultiPolygon geometry = { type: 'MultiPolygon', + // @ts-ignore coordinates: geometry.geometries.map((g) => g.coordinates), }; } else if (type === 'LineString') { // Combine all the LineStrings into a single MultiLineString geometry = { type: 'MultiLineString', + // @ts-ignore coordinates: geometry.geometries.map((g) => g.coordinates), }; } @@ -85,6 +91,7 @@ function getCleanedFeature(feature: Feature): Feature { } } + // @ts-ignore return { type: 'Feature', id, From 5e07db09e67600bed7eb94c06bb48da047426c29 Mon Sep 17 00:00:00 2001 From: Clay Anderson Date: Fri, 25 Sep 2020 08:12:12 -0600 Subject: [PATCH 3/6] Allow providing additional inputs to export component --- modules/editor/src/export-component.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/editor/src/export-component.tsx b/modules/editor/src/export-component.tsx index a58b1b9c4..01c2c4e22 100644 --- a/modules/editor/src/export-component.tsx +++ b/modules/editor/src/export-component.tsx @@ -44,6 +44,7 @@ const FooterRow = styled.div` export type ExportComponentProps = { features: any; onClose: () => unknown; + additionalInputs?: React.ReactNode; }; export function ExportComponent(props: ExportComponentProps) { @@ -113,6 +114,7 @@ export function ExportComponent(props: ExportComponentProps) { } /> + {props.additionalInputs || null} @@ -84,10 +92,7 @@ export function ExportComponent(props: ExportComponentProps) { style={{ backgroundColor: format === 'kml' ? 'rgb(0, 105, 217)' : 'rgb(90, 98, 94)', }} - onClick={() => { - setExportParams(toKml(geojson)); - setFormat('kml'); - }} + onClick={() => setFormat('kml')} > KML @@ -95,10 +100,7 @@ export function ExportComponent(props: ExportComponentProps) { style={{ backgroundColor: format === 'wkt' ? 'rgb(0, 105, 217)' : 'rgb(90, 98, 94)', }} - onClick={() => { - setExportParams(toWkt(geojson)); - setFormat('wkt'); - }} + onClick={() => setFormat('wkt')} > WKT From 6c2c8392520ba6629d80aeca76af5c6d250ae107 Mon Sep 17 00:00:00 2001 From: Clay Anderson Date: Fri, 25 Sep 2020 15:11:17 -0600 Subject: [PATCH 5/6] Ability to set export filename --- examples/editor/example.js | 16 +- modules/editor/src/export-component.tsx | 39 ++- modules/editor/src/export-modal.tsx | 6 +- modules/editor/src/lib/exporter.ts | 89 ++----- modules/editor/src/toolbox.tsx | 49 ++-- modules/editor/test/exporter.test.ts | 326 +++++++++++------------- modules/editor/test/importer.test.ts | 28 +- 7 files changed, 247 insertions(+), 306 deletions(-) diff --git a/examples/editor/example.js b/examples/editor/example.js index 599089737..f5ec673dc 100644 --- a/examples/editor/example.js +++ b/examples/editor/example.js @@ -17,7 +17,7 @@ const initialViewState = { }; export function Example() { - const [features, setFeatures] = React.useState({ + const [geoJson, setGeoJson] = React.useState({ type: 'FeatureCollection', features: [], }); @@ -25,11 +25,11 @@ export function Example() { const [mode, setMode] = React.useState(() => ViewMode); const layer = new EditableGeoJsonLayer({ - data: features, + data: geoJson, mode, selectedFeatureIndexes, onEdit: ({ updatedData }) => { - setFeatures(updatedData); + setGeoJson(updatedData); }, }); @@ -47,12 +47,12 @@ export function Example() { - setFeatures({ - ...features, - features: [...features.features, ...featureCollection.features], + onImport={(imported) => + setGeoJson({ + ...geoJson, + features: [...geoJson.features, ...imported.features], }) } /> diff --git a/modules/editor/src/export-component.tsx b/modules/editor/src/export-component.tsx index b136d6068..f1150228f 100644 --- a/modules/editor/src/export-component.tsx +++ b/modules/editor/src/export-component.tsx @@ -42,38 +42,53 @@ const FooterRow = styled.div` `; export type ExportComponentProps = { - features: any; + geoJson: any; onClose: () => unknown; + filename?: string; additionalInputs?: React.ReactNode; }; -export function ExportComponent(props: ExportComponentProps) { - const geojson = props.features; +export function ExportComponent({ + geoJson, + onClose, + filename, + additionalInputs, +}: ExportComponentProps) { const [format, setFormat] = React.useState('geoJson'); + let filenameValue = filename; + if (!filenameValue) { + if (geoJson.type === 'FeatureCollection') { + filenameValue = 'features'; + } else { + // single feature + filenameValue = geoJson.properties.name || geoJson.id || 'feature'; + } + } + const exportParams = React.useMemo(() => { switch (format) { case 'geoJson': - return toGeoJson(geojson); + return toGeoJson(geoJson, filenameValue); case 'kml': - return toKml(geojson); + return toKml(geoJson, filenameValue); case 'wkt': - return toWkt(geojson); + return toWkt(geoJson, filenameValue); default: throw Error(`Unsupported format ${format}`); } - }, [geojson, format]); + }, [geoJson, format, filenameValue]); const tooMuch = exportParams.data.length > 500000; function copyData() { - copy(exportParams.data).then(() => props.onClose()); + copy(exportParams.data).then(() => onClose()); // TODO Design and add in a notifications banner for errors in the modal. // .catch(err => {alert(`Error copying to clipboard: `, err)}) } function downloadData() { downloadjs(new Blob([exportParams.data]), exportParams.filename, exportParams.mimetype); - props.onClose(); + onClose(); } return ( @@ -86,7 +101,7 @@ export function ExportComponent(props: ExportComponentProps) { }} onClick={() => setFormat('geoJson')} > - GeoJson + GeoJSON - + ); diff --git a/modules/editor/src/export-modal.tsx b/modules/editor/src/export-modal.tsx index fd46716b5..fe3e4d836 100644 --- a/modules/editor/src/export-modal.tsx +++ b/modules/editor/src/export-modal.tsx @@ -5,8 +5,10 @@ import { EditorModal } from './editor-modal'; import { ExportComponent } from './export-component'; export type ExportModalProps = { - features: AnyGeoJson; + geoJson: AnyGeoJson; onClose: () => unknown; + filename?: string; + additionalInputs?: React.ReactNode; }; export function ExportModal(props: ExportModalProps) { @@ -14,7 +16,7 @@ export function ExportModal(props: ExportModalProps) { } + content={} /> ); } diff --git a/modules/editor/src/lib/exporter.ts b/modules/editor/src/lib/exporter.ts index d773ded22..07c5c8e84 100644 --- a/modules/editor/src/lib/exporter.ts +++ b/modules/editor/src/lib/exporter.ts @@ -3,9 +3,7 @@ import tokml from '@maphubs/tokml'; import { stringify as stringifyWkt } from 'wellknown'; // @ts-ignore -import { Feature, AnyGeoJson, Geometry, PolygonalGeometry } from '@nebula.gl/edit-modes'; - -export const UNNAMED = '__unnamed_feature__'; +import { AnyGeoJson, Geometry, PolygonalGeometry } from '@nebula.gl/edit-modes'; export type ExportParameters = { data: string; @@ -13,52 +11,43 @@ export type ExportParameters = { mimetype: string; }; -export function toGeoJson(geojson: AnyGeoJson): ExportParameters { - const filename = `${getFilename(geojson)}.geojson`; - const prepped = prepareGeoJsonForExport(geojson); - +export function toGeoJson(geoJson: AnyGeoJson, filename: string): ExportParameters { return { - data: JSON.stringify(prepped, null, 2), - filename, + data: JSON.stringify(geoJson, null, 2), + filename: `${filename}.geojson`, mimetype: 'application/json', }; } -export function toKml(geojson: AnyGeoJson): ExportParameters { - const filename = `${getFilename(geojson)}.kml`; - const prepped = prepareGeoJsonForExport(geojson); - +export function toKml(geoJson: AnyGeoJson, filename: string): ExportParameters { // For some reason, google maps doesn't surface id unless it is in the properties // So, put it also in properties - if (prepped.type === 'FeatureCollection') { - prepped.features.forEach((f) => { + if (geoJson.type === 'FeatureCollection') { + geoJson.features.forEach((f) => { f.properties = f.properties || {}; }); } - const kmlString = tokml(prepped); + const kmlString = tokml(geoJson); - // kmlString = addIdToKml(prepped, kmlString); + // kmlString = addIdToKml(geoJson, kmlString); return { data: kmlString, - filename, + filename: `${filename}.kml`, mimetype: 'application/xml', }; } -export function toWkt(geojson: AnyGeoJson): ExportParameters { - const filename = `${getFilename(geojson)}.wkt`; - const prepped = prepareGeoJsonForExport(geojson); - +export function toWkt(geoJson: AnyGeoJson, filename: string): ExportParameters { let wkt = ''; - if (prepped.type === 'Feature') { + if (geoJson.type === 'Feature') { // @ts-ignore - wkt = stringifyWkt(prepped); + wkt = stringifyWkt(geoJson); } else { // feature collection wkt = ''; - for (const feature of prepped.features) { + for (const feature of geoJson.features) { // @ts-ignore wkt += `${stringifyWkt(feature)}\n`; } @@ -69,26 +58,23 @@ export function toWkt(geojson: AnyGeoJson): ExportParameters { return { data: wkt, - filename, + filename: `${filename}.wkt`, mimetype: 'text/plain', }; } -export function toStats(geojson: AnyGeoJson): ExportParameters { - const filename = `${getFilename(geojson)}.txt`; - const prepped = prepareGeoJsonForExport(geojson); - +export function toStats(geoJson: AnyGeoJson, filename: string): ExportParameters { let pointCount = 0; let ringCount = 0; let polygonCount = 0; let featureCount = 0; - if (prepped.type === 'Feature') { - const polygonStats = getPolygonalStats(prepped.geometry); + if (geoJson.type === 'Feature') { + const polygonStats = getPolygonalStats(geoJson.geometry); ({ pointCount, ringCount, polygonCount } = polygonStats); featureCount = 1; } else { - for (const feature of prepped.features) { + for (const feature of geoJson.features) { const polygonStats = getPolygonalStats(feature.geometry); pointCount += polygonStats.pointCount; ringCount += polygonStats.ringCount; @@ -104,7 +90,7 @@ Points: ${pointCount}`; return { data: stats, - filename, + filename: `${filename}.txt`, mimetype: 'text/plain', }; } @@ -142,38 +128,3 @@ function getPolygonalStats(geometry: Geometry) { polygonCount, }; } - -function getFilename(geojson) { - let filename = 'geojsonFeatures'; - if (geojson.type === 'Feature') { - filename = geojson.properties.name || UNNAMED; - } - return filename; -} - -function prepareGeoJsonForExport(geojson: AnyGeoJson): AnyGeoJson { - let forExport; - if (geojson.type === 'FeatureCollection') { - forExport = { - ...geojson, - features: geojson.features.map(prepareFeatureForExport), - }; - } else { - forExport = prepareFeatureForExport(geojson); - } - - return forExport; -} - -function prepareFeatureForExport(feature: Feature): Feature { - const prepped = { - ...feature, - properties: { - ...feature.properties, - name: feature.properties.name || UNNAMED, - description: feature.properties.description || '', - }, - }; - - return prepped; -} diff --git a/modules/editor/src/toolbox.tsx b/modules/editor/src/toolbox.tsx index c84166f01..e22f8b934 100644 --- a/modules/editor/src/toolbox.tsx +++ b/modules/editor/src/toolbox.tsx @@ -12,9 +12,9 @@ const Tools = styled.div` right: 10px; `; -const Button = styled.span` +const Button = styled.button<{ active?: boolean }>` color: #fff; - background-color: rgb(90, 98, 94); + background: ${({ active }) => (active ? 'rgb(0, 105, 217)' : 'rgb(90, 98, 94)')}; font-size: 1em; font-weight: 400; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, @@ -24,13 +24,16 @@ const Button = styled.span` border-radius: 0.25em; margin: 0.05em; padding: 0.1em 0.2em; + :hover { + background: rgb(128, 137, 133); + } `; export type Props = { mode: any; - features: any; - onSetMode: (arg0: any) => unknown; - onImport: (arg0: any) => unknown; + geoJson: any; + onSetMode: (mode: any) => unknown; + onImport: (imported: any) => unknown; }; const MODE_BUTTONS = [ @@ -40,41 +43,55 @@ const MODE_BUTTONS = [ { mode: DrawPolygonMode, content: 'Draw Polygon' }, ]; -export function Toolbox(props: Props) { +export function Toolbox({ mode, geoJson, onSetMode, onImport }: Props) { // Initialize to zero index on load as nothing is active. const [showImport, setShowImport] = React.useState(false); const [showExport, setShowExport] = React.useState(false); + const [whatToExport, setWhatToExport] = React.useState(geoJson); + return ( <> {MODE_BUTTONS.map((modeButton, i) => ( ))} - - + + + {showImport && ( { - props.onImport(geojson); + onImport={(imported) => { + onImport(imported); setShowImport(false); }} onClose={() => setShowImport(false)} /> )} - {showExport && setShowExport(false)} />} + {showExport && setShowExport(false)} />} ); } diff --git a/modules/editor/test/exporter.test.ts b/modules/editor/test/exporter.test.ts index 31e14b4e8..ca5d67853 100644 --- a/modules/editor/test/exporter.test.ts +++ b/modules/editor/test/exporter.test.ts @@ -1,63 +1,45 @@ /* eslint-env jest */ import { Feature, FeatureCollection } from '@nebula.gl/edit-modes'; -import { toGeoJson, toKml, toWkt, toStats, UNNAMED } from '../src/lib/exporter'; +import { toGeoJson, toKml, toWkt, toStats } from '../src/lib/exporter'; import { createRandomFeature } from './utils/test-features'; -let unsavedNamedFeature: Feature; -let unsavedUnnamedFeature: Feature; +let feature: Feature; let featureCollection: FeatureCollection; beforeEach(() => { - unsavedNamedFeature = createRandomFeature(); - unsavedNamedFeature.properties.name = 'hamster'; - unsavedNamedFeature.properties.description = 'i am a hamster geofence'; + feature = createRandomFeature(); + feature.properties.name = 'My Feature'; - unsavedUnnamedFeature = createRandomFeature(); - delete unsavedUnnamedFeature.properties.name; + const feature2 = createRandomFeature(); + feature2.properties.name = 'My Other Feature'; featureCollection = { type: 'FeatureCollection', properties: {}, - features: [unsavedNamedFeature, unsavedUnnamedFeature], + features: [feature, feature2], }; }); describe('toGeoJson()', () => { - test('when unsaved, named Feature', () => { - const expectedFilename = `${unsavedNamedFeature.properties.name}.geojson`; + test('feature', () => { + const expectedFilename = `myfile.geojson`; const expectedMimeType = 'application/json'; - const actual = toGeoJson(unsavedNamedFeature); + const actual = toGeoJson(feature, 'myfile'); const actualParsed = JSON.parse(actual.data); expect(actual.filename).toEqual(expectedFilename); expect(actual.mimetype).toEqual(expectedMimeType); - expect(actualParsed.properties.name).toEqual(unsavedNamedFeature.properties.name); - expect(actualParsed.properties.description).toEqual(unsavedNamedFeature.properties.description); + expect(actualParsed.properties.name).toEqual(feature.properties.name); }); - test('when unsaved, unnamed Feature', () => { - const expectedFilename = `${UNNAMED}.geojson`; - const expectedMimeType = 'application/json'; - - const actual = toGeoJson(unsavedUnnamedFeature); - const actualParsed = JSON.parse(actual.data); - - expect(actual.filename).toEqual(expectedFilename); - expect(actual.mimetype).toEqual(expectedMimeType); - - expect(actualParsed.properties.name).toEqual(UNNAMED); - expect(actualParsed.properties.description).toEqual(''); - expect(actualParsed.id).toBeUndefined(); - }); - - test('when feature collection', () => { + test('feature collection', () => { const expectedFilename = `geojsonFeatures.geojson`; const expectedMimeType = 'application/json'; - const actual = toGeoJson(featureCollection); + const actual = toGeoJson(featureCollection, 'geojsonFeatures'); const actualParsed: FeatureCollection = JSON.parse(actual.data); expect(actual.filename).toEqual(expectedFilename); @@ -66,132 +48,117 @@ describe('toGeoJson()', () => { expect(actualParsed.features[0].properties.name).toEqual( featureCollection.features[0].properties.name ); - - expect(actualParsed.features[1].properties.name).toEqual(UNNAMED); }); }); describe('toKml()', () => { - test('when unsaved, named Feature', () => { - const expectedFilename = `${unsavedNamedFeature.properties.name}.kml`; - const expectedMimeType = 'application/xml'; - - const actual = toKml(unsavedNamedFeature); - - expect(actual.filename).toEqual(expectedFilename); - expect(actual.mimetype).toEqual(expectedMimeType); - - expect(actual.data).toContain(`${unsavedNamedFeature.properties.name}`); - - expect(actual.data).toContain( - `${unsavedNamedFeature.properties.description}` - ); - }); - - test('when unsaved, unnamed Feature', () => { - const expectedFilename = `${UNNAMED}.kml`; + test('feature', () => { + const expectedFilename = `myfile.kml`; const expectedMimeType = 'application/xml'; - const actual = toKml(unsavedUnnamedFeature); + const actual = toKml(feature, 'myfile'); expect(actual.filename).toEqual(expectedFilename); expect(actual.mimetype).toEqual(expectedMimeType); - expect(actual.data).toContain(`${UNNAMED}`); + expect(actual.data).toContain(`${feature.properties.name}`); }); }); describe('toWkt()', () => { - test('when feature', () => { + test('feature', () => { const expectedFilename = `llamallama.wkt`; const expectedMimeType = 'text/plain'; const expectedData = 'POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))'; - const actual = toWkt({ - type: 'Feature', - properties: { name: 'llamallama' }, - id: 'abc', - geometry: { - type: 'Polygon', - coordinates: [ - [ - [30, 10], - [40, 40], - [20, 40], - [10, 20], - [30, 10], + const actual = toWkt( + { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [30, 10], + [40, 40], + [20, 40], + [10, 20], + [30, 10], + ], ], - ], + }, }, - }); + 'llamallama' + ); expect(actual.filename).toEqual(expectedFilename); expect(actual.mimetype).toEqual(expectedMimeType); expect(actual.data).toEqual(expectedData); }); - test('when feature collection', () => { + test('feature collection', () => { const expectedFilename = `geojsonFeatures.wkt`; const expectedMimeType = 'text/plain'; const expectedData = 'POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))\nPOLYGON ((3 1, 4 4, 2 4, 1 2, 3 1))'; - const actual = toWkt({ - type: 'FeatureCollection', - properties: {}, - features: [ - { - type: 'Feature', - properties: { name: 'llamallama1' }, - id: 'abc', - geometry: { - type: 'Polygon', - coordinates: [ - [ - [30, 10], - [40, 40], - [20, 40], - [10, 20], - [30, 10], + const actual = toWkt( + { + type: 'FeatureCollection', + properties: {}, + features: [ + { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [30, 10], + [40, 40], + [20, 40], + [10, 20], + [30, 10], + ], ], - ], + }, }, - }, - { - type: 'Feature', - properties: { name: 'llamallama2' }, - id: 'def', - geometry: { - type: 'Polygon', - coordinates: [ - [ - [3, 1], - [4, 4], - [2, 4], - [1, 2], - [3, 1], + { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [3, 1], + [4, 4], + [2, 4], + [1, 2], + [3, 1], + ], ], - ], + }, }, - }, - ], - }); + ], + }, + 'geojsonFeatures' + ); expect(actual.filename).toEqual(expectedFilename); expect(actual.mimetype).toEqual(expectedMimeType); expect(actual.data).toEqual(expectedData); }); - test('when empty feature collection', () => { + test('empty feature collection', () => { const expectedFilename = `geojsonFeatures.wkt`; const expectedMimeType = 'text/plain'; const expectedData = ''; - const actual = toWkt({ - type: 'FeatureCollection', - properties: {}, - features: [], - }); + const actual = toWkt( + { + type: 'FeatureCollection', + properties: {}, + features: [], + }, + 'geojsonFeatures' + ); expect(actual.filename).toEqual(expectedFilename); expect(actual.mimetype).toEqual(expectedMimeType); @@ -201,96 +168,95 @@ describe('toWkt()', () => { describe('toStats()', () => { test('when feature', () => { - const expectedFilename = `llamallama.txt`; + const expectedFilename = `mystats.txt`; const expectedMimeType = 'text/plain'; const expectedData = 'Features: 1\nPolygons: 1\nRings: 1\nPoints: 5'; - const actual = toStats({ - type: 'Feature', - properties: { name: 'llamallama' }, - id: 'abc', - geometry: { - type: 'Polygon', - coordinates: [ - [ - [30, 10], - [40, 40], - [20, 40], - [10, 20], - [30, 10], + const actual = toStats( + { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [30, 10], + [40, 40], + [20, 40], + [10, 20], + [30, 10], + ], ], - ], + }, }, - }); + 'mystats' + ); expect(actual.filename).toEqual(expectedFilename); expect(actual.mimetype).toEqual(expectedMimeType); expect(actual.data).toEqual(expectedData); }); - test('when feature collection', () => { - const expectedFilename = `geojsonFeatures.txt`; + test('feature collection', () => { + const expectedFilename = `mystats.txt`; const expectedMimeType = 'text/plain'; const expectedData = 'Features: 2\nPolygons: 3\nRings: 4\nPoints: 20'; - const actual = toStats({ - type: 'FeatureCollection', - properties: {}, - features: [ - { - type: 'Feature', - properties: { name: 'llamallama1' }, - id: 'abc', - geometry: { - type: 'Polygon', - coordinates: [ - [ - [30, 10], - [40, 40], - [20, 40], - [10, 20], - [30, 10], - ], - ], - }, - }, - { - type: 'Feature', - properties: { name: 'llamallama2' }, - id: 'def', - geometry: { - type: 'MultiPolygon', - coordinates: [ - [ + const actual = toStats( + { + type: 'FeatureCollection', + features: [ + { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ [ - [3, 1], - [4, 4], - [2, 4], - [1, 2], - [3, 1], + [30, 10], + [40, 40], + [20, 40], + [10, 20], + [30, 10], ], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'MultiPolygon', + coordinates: [ [ - [3.5, 3.5], - [2, 3.5], - [1.5, 2], - [2.5, 1.5], - [3.5, 3.5], + [ + [3, 1], + [4, 4], + [2, 4], + [1, 2], + [3, 1], + ], + [ + [3.5, 3.5], + [2, 3.5], + [1.5, 2], + [2.5, 1.5], + [3.5, 3.5], + ], ], - ], - [ [ - [13, 11], - [14, 14], - [12, 14], - [11, 12], - [13, 11], + [ + [13, 11], + [14, 14], + [12, 14], + [11, 12], + [13, 11], + ], ], ], - ], + }, }, - }, - ], - }); + ], + }, + 'mystats' + ); expect(actual.filename).toEqual(expectedFilename); expect(actual.mimetype).toEqual(expectedMimeType); diff --git a/modules/editor/test/importer.test.ts b/modules/editor/test/importer.test.ts index f86b775d5..760fb07a9 100644 --- a/modules/editor/test/importer.test.ts +++ b/modules/editor/test/importer.test.ts @@ -4,7 +4,7 @@ import sinon from 'sinon'; import { Feature, FeatureCollection } from '@nebula.gl/edit-modes'; import { parseImport } from '../src/lib/importer'; -import { toKml, UNNAMED } from '../src/lib/exporter'; +import { toKml } from '../src/lib/exporter'; import { createRandomFeature, @@ -14,16 +14,16 @@ import { createRandomMultiLineString, } from './utils/test-features'; -let unsavedFeature: Feature; +let feature: Feature; let featureCollection: FeatureCollection; beforeEach(() => { - unsavedFeature = createRandomFeature(); + feature = createRandomFeature(); featureCollection = { type: 'FeatureCollection', properties: {}, - features: [unsavedFeature], + features: [feature], }; }); @@ -31,13 +31,13 @@ describe('parseImport()', () => { describe('GeoJSON Feature string', () => { let importData: any; beforeEach(async () => { - importData = await parseImport(JSON.stringify(unsavedFeature)); + importData = await parseImport(JSON.stringify(feature)); }); test('parses feature', () => { expect(importData.valid).toEqual(true); expect(importData.type).toEqual('GeoJSON'); - expect(importData.features[0].properties.name).toEqual(unsavedFeature.properties.name); + expect(importData.features[0].properties.name).toEqual(feature.properties.name); }); }); @@ -58,28 +58,18 @@ describe('parseImport()', () => { featureCollection.features.map((f) => f.geometry) ); }); - - test('removes protected properties', () => { - for (const feature of importData.features) { - expect(feature.properties.entity_type).toBeUndefined(); - } - }); }); describe('KML string', () => { let importData: any; beforeEach(async () => { - importData = await parseImport(toKml(featureCollection).data); + importData = await parseImport(toKml(featureCollection, 'filename').data); }); test('parses features', () => { expect(importData.valid).toEqual(true); expect(importData.type).toEqual('KML'); - expect(importData.features.map((f) => f.properties.name)).toEqual( - featureCollection.features.map((f) => f.properties.name || UNNAMED) - ); - expect(importData.features.map((f) => f.geometry)).toEqual( featureCollection.features.map((f) => f.geometry) ); @@ -116,7 +106,7 @@ describe('parseImport()', () => { describe('GeoJSON file', () => { let importData: any; beforeEach(async () => { - const file = new File([JSON.stringify(unsavedFeature)], 'my.geojson', { + const file = new File([JSON.stringify(feature)], 'my.geojson', { type: 'text/plain;charset=utf-8;', }); importData = await parseImport(file); @@ -125,7 +115,7 @@ describe('parseImport()', () => { test('parses feature', () => { expect(importData.valid).toEqual(true); expect(importData.type).toEqual('GeoJSON'); - expect(importData.features[0].properties.name).toEqual(unsavedFeature.properties.name); + expect(importData.features[0].properties.name).toEqual(feature.properties.name); }); }); From 7b574e0b1ccf6b806c1e68e6f02740759029dace Mon Sep 17 00:00:00 2001 From: Clay Anderson Date: Fri, 25 Sep 2020 15:14:39 -0600 Subject: [PATCH 6/6] Revert test code --- modules/editor/src/toolbox.tsx | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/modules/editor/src/toolbox.tsx b/modules/editor/src/toolbox.tsx index e22f8b934..f639ebb0b 100644 --- a/modules/editor/src/toolbox.tsx +++ b/modules/editor/src/toolbox.tsx @@ -48,8 +48,6 @@ export function Toolbox({ mode, geoJson, onSetMode, onImport }: Props) { const [showImport, setShowImport] = React.useState(false); const [showExport, setShowExport] = React.useState(false); - const [whatToExport, setWhatToExport] = React.useState(geoJson); - return ( <> @@ -65,22 +63,7 @@ export function Toolbox({ mode, geoJson, onSetMode, onImport }: Props) { ))} - - + {showImport && ( setShowImport(false)} /> )} - {showExport && setShowExport(false)} />} + {showExport && setShowExport(false)} />} ); }