Skip to content

Commit

Permalink
Merge 7b574e0 into ade9ca3
Browse files Browse the repository at this point in the history
  • Loading branch information
supersonicclay committed Sep 25, 2020
2 parents ade9ca3 + 7b574e0 commit 4c51127
Show file tree
Hide file tree
Showing 8 changed files with 249 additions and 317 deletions.
16 changes: 8 additions & 8 deletions examples/editor/example.js
Expand Up @@ -17,19 +17,19 @@ const initialViewState = {
};

export function Example() {
const [features, setFeatures] = React.useState({
const [geoJson, setGeoJson] = React.useState({
type: 'FeatureCollection',
features: [],
});
const [selectedFeatureIndexes] = React.useState([]);
const [mode, setMode] = React.useState(() => ViewMode);

const layer = new EditableGeoJsonLayer({
data: features,
data: geoJson,
mode,
selectedFeatureIndexes,
onEdit: ({ updatedData }) => {
setFeatures(updatedData);
setGeoJson(updatedData);
},
});

Expand All @@ -47,12 +47,12 @@ export function Example() {
</DeckGL>
<Toolbox
mode={mode}
features={features}
geoJson={geoJson}
onSetMode={setMode}
onImport={(featureCollection) =>
setFeatures({
...features,
features: [...features.features, ...featureCollection.features],
onImport={(imported) =>
setGeoJson({
...geoJson,
features: [...geoJson.features, ...imported.features],
})
}
/>
Expand Down
59 changes: 39 additions & 20 deletions modules/editor/src/export-component.tsx
Expand Up @@ -42,26 +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;
const [exportParams, setExportParams] = React.useState(toGeoJson(geojson));
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, filenameValue);
case 'kml':
return toKml(geoJson, filenameValue);
case 'wkt':
return toWkt(geoJson, filenameValue);
default:
throw Error(`Unsupported format ${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 (
Expand All @@ -72,32 +99,23 @@ export function ExportComponent(props: ExportComponentProps) {
style={{
backgroundColor: format === 'geoJson' ? 'rgb(0, 105, 217)' : 'rgb(90, 98, 94)',
}}
onClick={() => {
setExportParams(toGeoJson(geojson));
setFormat('geoJson');
}}
onClick={() => setFormat('geoJson')}
>
GeoJson
GeoJSON
</Button>
<Button
style={{
backgroundColor: format === 'kml' ? 'rgb(0, 105, 217)' : 'rgb(90, 98, 94)',
}}
onClick={() => {
setExportParams(toKml(geojson));
setFormat('kml');
}}
onClick={() => setFormat('kml')}
>
KML
</Button>
<Button
style={{
backgroundColor: format === 'wkt' ? 'rgb(0, 105, 217)' : 'rgb(90, 98, 94)',
}}
onClick={() => {
setExportParams(toWkt(geojson));
setFormat('wkt');
}}
onClick={() => setFormat('wkt')}
>
WKT
</Button>
Expand All @@ -113,14 +131,15 @@ export function ExportComponent(props: ExportComponentProps) {
}
/>
</ExportArea>
{additionalInputs || null}
<FooterRow>
<Button style={{ backgroundColor: 'rgb(0, 105, 217)' }} onClick={downloadData}>
Download
</Button>
<Button style={{ backgroundColor: 'rgb(0, 105, 217)' }} onClick={copyData}>
Copy
</Button>
<Button onClick={props.onClose}>Cancel</Button>
<Button onClick={onClose}>Cancel</Button>
</FooterRow>
</>
);
Expand Down
6 changes: 4 additions & 2 deletions modules/editor/src/export-modal.tsx
Expand Up @@ -5,16 +5,18 @@ 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) {
return (
<EditorModal
onClose={props.onClose}
title={'Export'}
content={<ExportComponent features={props.features} onClose={props.onClose} />}
content={<ExportComponent {...props} />}
/>
);
}
89 changes: 20 additions & 69 deletions modules/editor/src/lib/exporter.ts
Expand Up @@ -3,62 +3,51 @@
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;
filename: string;
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`;
}
Expand All @@ -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;
Expand All @@ -104,7 +90,7 @@ Points: ${pointCount}`;

return {
data: stats,
filename,
filename: `${filename}.txt`,
mimetype: 'text/plain',
};
}
Expand Down Expand Up @@ -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;
}
10 changes: 7 additions & 3 deletions modules/editor/src/lib/importer.ts
Expand Up @@ -53,9 +53,11 @@ function getCleanedFeatures(geojson: AnyGeoJson): Feature[] {
}

function getCleanedFeature(feature: Feature): Feature {
let geometry = feature.geometry;
const { id } = feature;
// reduce null-checking
const properties = feature.properties || {};

let geometry = feature.geometry;
// @ts-ignore
if (geometry.type === 'GeometryCollection' && geometry.geometries.length === 1) {
// There's only one geometry
Expand All @@ -69,14 +71,14 @@ function getCleanedFeature(feature: Feature): Feature {
// 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
Expand All @@ -88,9 +90,11 @@ function getCleanedFeature(feature: Feature): Feature {
throw Error('GeometryCollection geometry type not yet supported');
}
}

// @ts-ignore
return {
type: 'Feature',
id,
geometry,
properties,
};
Expand Down

0 comments on commit 4c51127

Please sign in to comment.