Skip to content

Instantly share code, notes, and snippets.

View tmcw's full-sized avatar
💭
merging your prs

Tom MacWright tmcw

💭
merging your prs
View GitHub Profile
export default 42;
@tmcw
tmcw / README.md
Created December 28, 2022 17:00
PMTiles to SVG

Request a single tile from PMTiles, use the vector-tiles module to read it, the d3-geo tools to turn it into a string. d3-geo is probably unnecessary.

This approach feels limited:

Requires restitching tiles if more than one. Limited attributes. Any tile-based solution is going to make compromises in favor of responsiveness and lightweight data. What this wants is completeness and non-sliced data.

import { Context } from "https://edge.netlify.com/";
const DOMAIN = "macwright.com";
const USERS = new Map([
[
`acct:photos@macwright.com`,
{
subject: `acct:photos@${DOMAIN}`,
aliases: [],
import { Portal } from "@radix-ui/react-portal";
import clsx from "clsx";
import { useAtom } from "jotai";
import { atomWithMachine } from "jotai/xstate";
import { useRouter } from "next/router";
import { useEffect } from "react";
import { assign, createMachine } from "xstate";
import clamp from "lodash/clamp";
export function useMapKeybindings() {
const rep = usePersistence();
const historyControl = rep.useHistoryControl();
const transact = rep.useTransact();
useHotkeys(
"Command+z, Ctrl+z",
(e) => {
e.preventDefault();
historyControl("undo");
import { createContext, useContext } from "react";
import type { IPersistence } from "app/lib/persistence/ipersistence";
const notInContext = {} as IPersistence;
export const PersistenceContext = createContext<IPersistence>(notInContext);
export function usePersistence() {
return useContext(PersistenceContext);
}
export interface MomentInput {
note?: string;
putFeatures: IWrappedFeatureInput[];
deleteFeatures: IWrappedFeature["id"][];
putFolders: IFolderInput[];
deleteFolders: IFolder["id"][];
}
export interface IPersistence {
useHistoryControl(): (direction: "undo" | "redo") => Promise<void>;
{
// User ID
"id": 1,
"type": "Feature",
"properties": {
// System ID
"@id": "d13b47c0-23fb-11ed-a50b-8b6722c6e6fe"
},
"geometry": {
"type": "Point",
import { wktToGeoJSON, geoJSONToWkt } from "betterknown";
import proj4 from "proj4";
// Converting & reprojecting an EWKT string
wktToGeoJSON(`SRID=3857;POINT(-400004.3 60000.1)`, {
proj: proj4,
});
// Converting GeoJSON to WKT
geoJSONToWkt({

A faster format in Placemark

Placemark uses GeoJSON, and JSON, everywhere. In the database, features are GeoJSON. When we're sending features into Mapbox GL, it's GeoJSON getting sent with postMessage.

GeoJSON, of course, has its issues:

  • Sending GeoJSON features across to the WebWorker that cuts tiles for Mapbox GL JS is a major bottleneck for the core editing experience.
  • GeoJSON in resident memory is bigger than it needs to be. GeoJSON's coordinate arrays, especially, are an issue - flat arrays would be much more compact.
  • Mapbox GL JS itself has to cut GeoJSON into tiles, which requires some transformation - it creates another flat representation of geometry coordinates.