Skip to content

Commit

Permalink
paste geojson features
Browse files Browse the repository at this point in the history
  • Loading branch information
missinglink committed Sep 24, 2024
1 parent 60e77dc commit f8eb032
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 67 deletions.
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"@turf/great-circle": "^7.1.0",
"maplibre-gl": "^4.6.0",
"protomaps-themes-base": "^3.1.0",
"s2js": "^1.43.5",
"s2js": "^1.43.6",
"solid-js": "^1.8.20",
"terra-draw": "^1.0.0-beta.1"
},
Expand Down
151 changes: 89 additions & 62 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -226,51 +226,96 @@ function App() {
const [cellUnionText, setCellUnionText] = createSignal("");
const [cellUnionLength, setCellUnionLength] = createSignal(0);
const [loadError, setLoadError] = createSignal("");
let textArea: HTMLTextAreaElement | undefined;

const computeCoveringForDraw = () => {
const snapshot = draw!.getSnapshot() as Feature<Polygon>[];

const covering = getCovering(regionCoverer, snapshot);
displayCovering(covering);
// list of features currently loaded in the map view
const [features, setFeatures] = createSignal<Feature[]>([], {
equals: () => false,
});
const clearFeatures = () => setFeatures((features) => features.slice(0, 0));
const addFeatures = (additions: Feature[]) => {
setFeatures((features) => {
for (let feat of additions) {
const hash = JSON.stringify(feat);
const duplicate = features.some((f) => JSON.stringify(f) === hash);
if (!duplicate) features.push(feat);
}
return features;
});
};

const displayCovering = (covering: s2.CellUnion) => {
(map!.getSource("covering") as maplibregl.GeoJSONSource).setData(
getCellVisualization(covering),
);
// re-render covering when features / tokens change
createEffect(() =>
displayCovering(getCovering(regionCoverer, features()), features()),
);

let textArea: HTMLTextAreaElement | undefined;
let jsonArea: HTMLTextAreaElement | undefined;

const displayCovering = (covering: s2.CellUnion, features?: Feature[]) => {
if (!map) return;

const source: maplibregl.GeoJSONSource = map.getSource("covering")!;
source.setData(getCellVisualization(covering));

setCellUnionText([...covering].map((c) => s2.cellid.toToken(c)).join(", "));
setCellUnionLength(covering.length);

// dont zoom if all features are from terradraw
if (
features &&
features.every((f) => f.id && f.id.toString().length === 36)
) {
return;
}

const rect = covering.rectBound();
map.fitBounds(
[
[s1.angle.degrees(rect.lng.lo), s1.angle.degrees(rect.lat.lo)],
[s1.angle.degrees(rect.lng.hi), s1.angle.degrees(rect.lat.hi)],
],
{ padding: 50 },
);
};

const loadCoveringFromText = () => {
let rect = s2.Rect.emptyRect();

draw.clear();
if (!textArea) return;

// attempt to parse as JSON
let json;
let jsonError;

Check failure on line 286 in src/App.tsx

View workflow job for this annotation

GitHub Actions / gh_pages

'jsonError' is declared but its value is never read.
try {
json = JSON.parse(textArea.value) as Feature;
} catch (e: any) {
jsonError = e.message.split(":")[0];
}

if (json)
try {
const covering = new s2.CellUnion(
...textArea.value.split(", ").map(s2.cellid.fromToken),
);

draw.clear();
clearFeatures();
displayCovering(covering);
setLoadError("");
} catch (e: any) {
setLoadError(e.message);
}
};

const loadGeoJsonFromText = () => {
if (!jsonArea) return;

try {
const covering = new s2.CellUnion(
...textArea.value.split(", ").map((token) => {
const cellid = s2.cellid.fromToken(token);
const cell = s2.Cell.fromCellID(cellid);
rect = rect.union(cell.rectBound());
return cellid;
}),
);

displayCovering(covering);
map.fitBounds(
[
[s1.angle.degrees(rect.lng.lo), s1.angle.degrees(rect.lat.lo)],
[s1.angle.degrees(rect.lng.hi), s1.angle.degrees(rect.lat.hi)],
],
{ padding: 50 },
);
const feature = JSON.parse(jsonArea.value) as Feature;
if (feature?.type !== "Feature") throw new Error("Invalid Feature");
clearFeatures();
addFeatures([feature]);
setLoadError("");
} catch (e: any) {
setLoadError(e.message);
setLoadError(e.message.split(":")[0]);
}
};

Expand All @@ -280,14 +325,12 @@ function App() {
maxLevel: maxLevel(),
maxCells: maxCells(),
});
if (draw) {
computeCoveringForDraw();
}
displayCovering(getCovering(regionCoverer, features()), features());
});

const clear = () => {
draw.clear();
computeCoveringForDraw();
clearFeatures();
startDrawMode("rectangle");
};

Expand Down Expand Up @@ -337,7 +380,7 @@ function App() {

draw.on("finish", () => {
startDrawMode("render");
computeCoveringForDraw();
addFeatures(draw.getSnapshot());
});

draw.start();
Expand Down Expand Up @@ -443,31 +486,6 @@ function App() {
);
}
});

// secret paste polygon from clipboard command
var keyMap: { [name: string]: boolean } = {};
window.onkeydown = window.onkeyup = async (e) => {
keyMap[e.code] = e.type == "keydown";
if (e.type != "keydown") return;

// cntrl + shift + v
if (!["ControlLeft", "ShiftLeft", "KeyV"].every((c) => keyMap[c])) return;

try {
console.error("secret keyboard shortcut activated!!");
if (!navigator?.clipboard) throw new Error("clipboard API unavailable");
const clip = await navigator.clipboard.readText();
const feature = JSON.parse(clip);
if (feature?.type !== "Feature") {
throw new Error("invalid feature");
}
const covering = getCovering(regionCoverer, [feature]);
displayCovering(covering);
} catch (e) {
console.error("clipboard paste failed");
console.error(e);
}
};
});

return (
Expand Down Expand Up @@ -538,7 +556,16 @@ function App() {
) : (
<span>{cellUnionLength()} cells</span>
)}
<button onClick={loadCoveringFromText}>Load Text</button>
<button onClick={loadCoveringFromText}>Load Tokens</button>
</div>
<textarea ref={jsonArea} rows="5" value={geojsonText()}></textarea>

Check failure on line 561 in src/App.tsx

View workflow job for this annotation

GitHub Actions / gh_pages

Cannot find name 'geojsonText'.
<div class="textarealabel">
{geojsonLoadError() ? (

Check failure on line 563 in src/App.tsx

View workflow job for this annotation

GitHub Actions / gh_pages

Cannot find name 'geojsonLoadError'.
<span>{geojsonLoadError()}</span>

Check failure on line 564 in src/App.tsx

View workflow job for this annotation

GitHub Actions / gh_pages

Cannot find name 'geojsonLoadError'.
) : (
<span></span>
)}
<button onClick={loadGeoJsonFromText}>Load GeoJSON</button>
</div>
</div>
<div class="text">
Expand Down

0 comments on commit f8eb032

Please sign in to comment.