diff --git a/app/biome.json b/app/biome.json new file mode 100644 index 00000000..a3c305d0 --- /dev/null +++ b/app/biome.json @@ -0,0 +1,20 @@ +{ + "javascript": { + "formatter": { + "trailingComma": "es5" + } + }, + "formatter": { + "indentStyle": "space" + }, + "linter": { + "rules": { + "style": { + "useNamingConvention": {} + }, + "nursery": { + "noUnusedImports": {} + } + } + } +} diff --git a/app/package-lock.json b/app/package-lock.json index ed8c9885..b07df86d 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -28,6 +28,7 @@ "react-use": "^17.4.0" }, "devDependencies": { + "@biomejs/biome": "^1.5.3", "@maplibre/maplibre-gl-style-spec": "^19.3.1", "@types/d3-path": "^3.0.0", "@types/d3-scale-chromatic": "^3.0.0", @@ -448,6 +449,161 @@ "node": ">=6.9.0" } }, + "node_modules/@biomejs/biome": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.5.3.tgz", + "integrity": "sha512-yvZCa/g3akwTaAQ7PCwPWDCkZs3Qa5ONg/fgOUT9e6wAWsPftCjLQFPXBeGxPK30yZSSpgEmRCfpGTmVbUjGgg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "biome": "bin/biome" + }, + "engines": { + "node": ">=14.*" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/biome" + }, + "optionalDependencies": { + "@biomejs/cli-darwin-arm64": "1.5.3", + "@biomejs/cli-darwin-x64": "1.5.3", + "@biomejs/cli-linux-arm64": "1.5.3", + "@biomejs/cli-linux-arm64-musl": "1.5.3", + "@biomejs/cli-linux-x64": "1.5.3", + "@biomejs/cli-linux-x64-musl": "1.5.3", + "@biomejs/cli-win32-arm64": "1.5.3", + "@biomejs/cli-win32-x64": "1.5.3" + } + }, + "node_modules/@biomejs/cli-darwin-arm64": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.5.3.tgz", + "integrity": "sha512-ImU7mh1HghEDyqNmxEZBoMPr8SxekkZuYcs+gynKlNW+TALQs7swkERiBLkG9NR0K1B3/2uVzlvYowXrmlW8hw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.*" + } + }, + "node_modules/@biomejs/cli-darwin-x64": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.5.3.tgz", + "integrity": "sha512-vCdASqYnlpq/swErH7FD6nrFz0czFtK4k/iLgj0/+VmZVjineFPgevOb+Sr9vz0tk0GfdQO60bSpI74zU8M9Dw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.*" + } + }, + "node_modules/@biomejs/cli-linux-arm64": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.5.3.tgz", + "integrity": "sha512-cupBQv0sNF1OKqBfx7EDWMSsKwRrBUZfjXawT4s6hKV6ALq7p0QzWlxr/sDmbKMLOaLQtw2Qgu/77N9rm+f9Rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.*" + } + }, + "node_modules/@biomejs/cli-linux-arm64-musl": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.5.3.tgz", + "integrity": "sha512-DYuMizUYUBYfS0IHGjDrOP1RGipqWfMGEvNEJ398zdtmCKLXaUvTimiox5dvx4X15mBK5M2m8wgWUgOP1giUpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.*" + } + }, + "node_modules/@biomejs/cli-linux-x64": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.5.3.tgz", + "integrity": "sha512-YQrSArQvcv4FYsk7Q91Yv4uuu5F8hJyORVcv3zsjCLGkjIjx2RhjYLpTL733SNL7v33GmOlZY0eFR1ko38tuUw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.*" + } + }, + "node_modules/@biomejs/cli-linux-x64-musl": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.5.3.tgz", + "integrity": "sha512-UUHiAnlDqr2Y/LpvshBFhUYMWkl2/Jn+bi3U6jKuav0qWbbBKU/ByHgR4+NBxpKBYoCtWxhnmatfH1bpPIuZMw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.*" + } + }, + "node_modules/@biomejs/cli-win32-arm64": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.5.3.tgz", + "integrity": "sha512-HxatYH7vf/kX9nrD+pDYuV2GI9GV8EFo6cfKkahAecTuZLPxryHx1WEfJthp5eNsE0+09STGkKIKjirP0ufaZA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.*" + } + }, + "node_modules/@biomejs/cli-win32-x64": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.5.3.tgz", + "integrity": "sha512-fMvbSouZEASU7mZH8SIJSANDm5OqsjgtVXlbUqxwed6BP7uuHRSs396Aqwh2+VoW8fwTpp6ybIUoC9FrzB0kyA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.*" + } + }, "node_modules/@emotion/babel-plugin": { "version": "11.10.5", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.5.tgz", diff --git a/app/package.json b/app/package.json index b6ad01d0..f8a3b8fe 100644 --- a/app/package.json +++ b/app/package.json @@ -30,6 +30,7 @@ "react-use": "^17.4.0" }, "devDependencies": { + "@biomejs/biome": "^1.5.3", "@maplibre/maplibre-gl-style-spec": "^19.3.1", "@types/d3-path": "^3.0.0", "@types/d3-scale-chromatic": "^3.0.0", diff --git a/app/src/App.tsx b/app/src/App.tsx index 494b3511..099476eb 100644 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -1,11 +1,11 @@ +import * as DialogPrimitive from "@radix-ui/react-dialog"; +import { GitHubLogoIcon } from "@radix-ui/react-icons"; import React, { useState, useEffect } from "react"; -import { styled, globalStyles } from "./stitches.config"; import { PMTiles } from "../../js/index"; -import { GitHubLogoIcon } from "@radix-ui/react-icons"; -import * as DialogPrimitive from "@radix-ui/react-dialog"; +import { globalStyles, styled } from "./stitches.config"; -import Start from "./Start"; import Loader from "./Loader"; +import Start from "./Start"; const Header = styled("div", { height: "$4", @@ -57,15 +57,15 @@ const GIT_SHA = (import.meta.env.VITE_GIT_SHA || "").substr(0, 8); function App() { globalStyles(); - let [errorDisplay, setErrorDisplay] = useState(); - let [file, setFile] = useState(); - let [mapHashPassed, setMapHashPassed] = useState(false); + const [errorDisplay, setErrorDisplay] = useState(); + const [file, setFile] = useState(); + const [mapHashPassed, setMapHashPassed] = useState(false); // initial load useEffect(() => { const loadUrl = new URLSearchParams(location.search).get("url"); if (loadUrl) { - let initialValue = new PMTiles(loadUrl); + const initialValue = new PMTiles(loadUrl); setFile(initialValue); } if (location.hash.includes("map")) { @@ -84,7 +84,7 @@ function App() { // maintaining URL state useEffect(() => { const url = new URL(window.location.href); - if (file && file.source.getKey().startsWith("http")) { + if (file?.source.getKey().startsWith("http")) { url.searchParams.set("url", file.source.getKey()); history.pushState(null, "", url.toString()); } else { @@ -93,7 +93,7 @@ function App() { } }, [file]); - let clear = (event: React.MouseEvent) => { + const clear = (event: React.MouseEvent) => { event.preventDefault(); setFile(undefined); }; diff --git a/app/src/Inspector.tsx b/app/src/Inspector.tsx index b25f3600..cfdeb393 100644 --- a/app/src/Inspector.tsx +++ b/app/src/Inspector.tsx @@ -1,13 +1,12 @@ -import { useState, useEffect, useRef, Dispatch, SetStateAction } from "react"; -import { createPortal } from "react-dom"; -import { PMTiles, Entry, tileIdToZxy, TileType, Header } from "../../js/index"; -import { styled } from "./stitches.config"; -import Protobuf from "pbf"; -import { VectorTile, VectorTileFeature } from "@mapbox/vector-tile"; +import { VectorTile } from "@mapbox/vector-tile"; import { path } from "d3-path"; import { schemeSet3 } from "d3-scale-chromatic"; -import { useMeasure } from "react-use"; +import Protobuf from "pbf"; +import { Dispatch, SetStateAction, useEffect, useRef, useState } from "react"; import { UncontrolledReactSVGPanZoom } from "react-svg-pan-zoom"; +import { useMeasure } from "react-use"; +import { Entry, Header, PMTiles, TileType, tileIdToZxy } from "../../js/index"; +import { styled } from "./stitches.config"; const TableContainer = styled("div", { height: "calc(100vh - $4 - $4)", @@ -15,7 +14,7 @@ const TableContainer = styled("div", { width: "calc(100%/3)", }); -const SVGContainer = styled("div", { +const SvgContainer = styled("div", { width: "100%", height: "calc(100vh - $4 - $4)", }); @@ -45,7 +44,7 @@ const TileRow = (props: { entry: Entry; setSelectedEntry: (val: Entry | null) => void; }) => { - let [z, x, y] = tileIdToZxy(props.entry.tileId); + const [z, x, y] = tileIdToZxy(props.entry.tileId); return ( { @@ -59,7 +58,7 @@ const TileRow = (props: { {props.entry.offset} {props.entry.length} - {props.entry.runLength == 0 + {props.entry.runLength === 0 ? "directory" : `tile(${props.entry.runLength})`} @@ -80,7 +79,7 @@ interface Feature { properties: any; } -let smartCompare = (a: Layer, b: Layer): number => { +const smartCompare = (a: Layer, b: Layer): number => { if (a.name === "earth") return -4; if (a.name === "water") return -3; if (a.name === "natural") return -2; @@ -93,7 +92,7 @@ const FeatureSvg = (props: { feature: Feature; setSelectedFeature: Dispatch>; }) => { - let [highlighted, setHighlighted] = useState(false); + const [highlighted, setHighlighted] = useState(false); let fill = "none"; let stroke = ""; @@ -103,15 +102,15 @@ const FeatureSvg = (props: { stroke = highlighted ? "white" : "currentColor"; } - let mouseOver = () => { + const mouseOver = () => { setHighlighted(true); }; - let mouseOut = () => { + const mouseOut = () => { setHighlighted(false); }; - let mouseDown = () => { + const mouseDown = () => { props.setSelectedFeature(props.feature); }; @@ -125,7 +124,7 @@ const FeatureSvg = (props: { onMouseOver={mouseOver} onMouseOut={mouseOut} onMouseDown={mouseDown} - > + /> ); }; @@ -134,12 +133,12 @@ const LayerSvg = (props: { color: string; setSelectedFeature: Dispatch>; }) => { - let elems = props.layer.features.map((f, i) => ( + const elems = props.layer.features.map((f, i) => ( + /> )); return {elems}; }; @@ -153,8 +152,8 @@ const StyledFeatureProperties = styled("div", { }); const FeatureProperties = (props: { feature: Feature }) => { - let tmp: [string, string][] = []; - for (var key in props.feature.properties) { + const tmp: [string, string][] = []; + for (const key in props.feature.properties) { tmp.push([key, props.feature.properties[key]]); } @@ -180,44 +179,44 @@ const VectorPreview = (props: { entry: Entry; tileType: TileType; }) => { - let [layers, setLayers] = useState([]); - let [maxExtent, setMaxExtent] = useState(0); - let [selectedFeature, setSelectedFeature] = useState(null); - const Viewer = useRef(null); + const [layers, setLayers] = useState([]); + const [maxExtent, setMaxExtent] = useState(0); + const [selectedFeature, setSelectedFeature] = useState(null); + const viewer = useRef(null); const [ref, { width, height }] = useMeasure(); useEffect(() => { - Viewer.current!.zoomOnViewerCenter(0.1); + viewer.current?.zoomOnViewerCenter(0.1); }, []); useEffect(() => { - let fn = async (entry: Entry) => { - let [z, x, y] = tileIdToZxy(entry.tileId); - let resp = await props.file.getZxy(z, x, y); - - let tile = new VectorTile(new Protobuf(new Uint8Array(resp!.data))); - let newLayers = []; - let max_extent = 0; - for (let [name, layer] of Object.entries(tile.layers)) { - if (layer.extent > max_extent) { - max_extent = layer.extent; + const fn = async (entry: Entry) => { + const [z, x, y] = tileIdToZxy(entry.tileId); + const resp = await props.file.getZxy(z, x, y); + + const tile = new VectorTile(new Protobuf(new Uint8Array(resp!.data))); + const newLayers = []; + let maxExtent = 0; + for (const [name, layer] of Object.entries(tile.layers)) { + if (layer.extent > maxExtent) { + maxExtent = layer.extent; } - let features: Feature[] = []; - for (var i = 0; i < layer.length; i++) { - let feature = layer.feature(i); - let p = path(); - let geom = feature.loadGeometry(); + const features: Feature[] = []; + for (let i = 0; i < layer.length; i++) { + const feature = layer.feature(i); + const p = path(); + const geom = feature.loadGeometry(); if (feature.type === 1) { - for (let ring of geom) { - for (let pt of ring) { + for (const ring of geom) { + for (const pt of ring) { p.arc(pt.x, pt.y, 20, 0, 2 * Math.PI); } } } else { - for (let ring of geom) { + for (const ring of geom) { p.moveTo(ring[0].x, ring[0].y); - for (var j = 1; j < ring.length; j++) { + for (let j = 1; j < ring.length; j++) { p.lineTo(ring[j].x, ring[j].y); } if (feature.type === 3) { @@ -235,7 +234,7 @@ const VectorPreview = (props: { } newLayers.push({ features: features, name: name }); } - setMaxExtent(max_extent); + setMaxExtent(maxExtent); newLayers.sort(smartCompare); setLayers(newLayers); }; @@ -245,19 +244,19 @@ const VectorPreview = (props: { } }, [props.entry]); - let elems = layers.map((l, i) => ( + const elems = layers.map((l, i) => ( + /> )); return ( - + {elems} {selectedFeature ? : null} - + ); }; const RasterPreview = (props: { file: PMTiles; entry: Entry }) => { - let [imgSrc, setImageSrc] = useState(""); + const [imgSrc, setImageSrc] = useState(""); useEffect(() => { - let fn = async (entry: Entry) => { + const fn = async (entry: Entry) => { // TODO 0,0,0 is broken - let [z, x, y] = tileIdToZxy(entry.tileId); - let resp = await props.file.getZxy(z, x, y); - let blob = new Blob([resp!.data]); - var imageUrl = window.URL.createObjectURL(blob); + const [z, x, y] = tileIdToZxy(entry.tileId); + const resp = await props.file.getZxy(z, x, y); + const blob = new Blob([resp!.data]); + const imageUrl = window.URL.createObjectURL(blob); setImageSrc(imageUrl); }; @@ -290,12 +289,12 @@ const RasterPreview = (props: { file: PMTiles; entry: Entry }) => { } }, [props.entry]); - return ; + return raster tile; }; function getHashString(entry: Entry) { const [z, x, y] = tileIdToZxy(entry.tileId); - let hash = `${z}/${x}/${y}`; + const hash = `${z}/${x}/${y}`; const hashName = "inspector"; let found = false; @@ -318,9 +317,9 @@ function getHashString(entry: Entry) { } function Inspector(props: { file: PMTiles }) { - let [entryRows, setEntryRows] = useState([]); - let [selectedEntry, setSelectedEntryRaw] = useState(null); - let [header, setHeader] = useState
(null); + const [entryRows, setEntryRows] = useState([]); + const [selectedEntry, setSelectedEntryRaw] = useState(null); + const [header, setHeader] = useState
(null); function setSelectedEntry(val: Entry | null) { if (val && val.runLength > 0) { @@ -330,13 +329,13 @@ function Inspector(props: { file: PMTiles }) { } useEffect(() => { - let fn = async () => { - let header = await props.file.getHeader(); + const fn = async () => { + const header = await props.file.getHeader(); setHeader(header); if (header.specVersion < 3) { setEntryRows([]); } else if (selectedEntry !== null && selectedEntry.runLength === 0) { - let entries = await props.file.cache.getDirectory( + const entries = await props.file.cache.getDirectory( props.file.source, header.leafDirectoryOffset + selectedEntry.offset, selectedEntry.length, @@ -344,7 +343,7 @@ function Inspector(props: { file: PMTiles }) { ); setEntryRows(entries); } else if (selectedEntry === null) { - let entries = await props.file.cache.getDirectory( + const entries = await props.file.cache.getDirectory( props.file.source, header.rootDirectoryOffset, header.rootDirectoryLength, @@ -357,11 +356,11 @@ function Inspector(props: { file: PMTiles }) { fn(); }, [props.file, selectedEntry]); - let rows = entryRows.map((e, i) => ( - + const rows = entryRows.map((e, i) => ( + )); - let tilePreview =
; + let tilePreview =
; if (selectedEntry && header?.tileType) { if (selectedEntry.runLength === 0) { // do nothing diff --git a/app/src/Loader.tsx b/app/src/Loader.tsx index 53e69582..9cb3b9f7 100644 --- a/app/src/Loader.tsx +++ b/app/src/Loader.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from "react"; +import { useState } from "react"; import { PMTiles } from "../../js/index"; import { styled } from "./stitches.config"; @@ -6,7 +6,7 @@ import Inspector from "./Inspector"; import MaplibreMap from "./MaplibreMap"; import Metadata from "./Metadata"; -import { MagnifyingGlassIcon, ImageIcon } from "@radix-ui/react-icons"; +import { MagnifyingGlassIcon } from "@radix-ui/react-icons"; import * as ToolbarPrimitive from "@radix-ui/react-toolbar"; const StyledToolbar = styled(ToolbarPrimitive.Root, { @@ -28,7 +28,7 @@ const itemStyles = { fontSize: "$2", alignItems: "center", "&:hover": { backgroundColor: "$hover", color: "$white" }, - "&:focus": { position: "relative", boxShadow: `0 0 0 2px blue` }, + "&:focus": { position: "relative", boxShadow: "0 0 0 2px blue" }, }; const StyledLink = styled( @@ -71,9 +71,9 @@ const ToolbarToggleGroup = StyledToggleGroup; const ToolbarToggleItem = StyledToggleItem; function Loader(props: { file: PMTiles; mapHashPassed: boolean }) { - let [tab, setTab] = useState("maplibre"); + const [tab, setTab] = useState("maplibre"); - let view; + let view: any; if (tab === "maplibre") { view = ( diff --git a/app/src/MaplibreMap.tsx b/app/src/MaplibreMap.tsx index 775e481c..58c6e566 100644 --- a/app/src/MaplibreMap.tsx +++ b/app/src/MaplibreMap.tsx @@ -1,17 +1,17 @@ +import { + LayerSpecification, + StyleSpecification, +} from "@maplibre/maplibre-gl-style-spec"; +import { schemeSet3 } from "d3-scale-chromatic"; +import maplibregl from "maplibre-gl"; +import { MapGeoJSONFeature } from "maplibre-gl"; +import "maplibre-gl/dist/maplibre-gl.css"; +import baseTheme from "protomaps-themes-base"; import React, { useState, useEffect, useRef } from "react"; import { renderToString } from "react-dom/server"; -import { PMTiles, TileType } from "../../js/index"; import { Protocol } from "../../js/adapters"; +import { PMTiles, TileType } from "../../js/index"; import { styled } from "./stitches.config"; -import maplibregl from "maplibre-gl"; -import { MapGeoJSONFeature } from "maplibre-gl"; -import "maplibre-gl/dist/maplibre-gl.css"; -import { schemeSet3 } from "d3-scale-chromatic"; -import base_theme from "protomaps-themes-base"; -import { - StyleSpecification, - LayerSpecification, -} from "@maplibre/maplibre-gl-style-spec"; const INITIAL_ZOOM = 0; const INITIAL_LNG = 0; @@ -102,7 +102,7 @@ interface LayerVisibility { visible: boolean; } -interface PMTilesMetadata { +interface Metadata { name?: string; type?: string; tilestats?: unknown; @@ -187,12 +187,12 @@ const LayersVisibilityController = (props: { }; const rasterStyle = async (file: PMTiles): Promise => { - let header = await file.getHeader(); - let metadata = (await file.getMetadata()) as PMTilesMetadata; + const header = await file.getHeader(); + const metadata = (await file.getMetadata()) as Metadata; let layers: LayerSpecification[] = []; if (metadata.type !== "baselayer") { - layers = base_theme("basemap", "black"); + layers = baseTheme("basemap", "black"); } layers.push({ @@ -206,7 +206,7 @@ const rasterStyle = async (file: PMTiles): Promise => { sources: { source: { type: "raster", - tiles: ["pmtiles://" + file.source.getKey() + "/{z}/{x}/{y}"], + tiles: [`pmtiles://${file.source.getKey()}/{z}/{x}/{y}`], minzoom: header.minZoom, maxzoom: header.maxZoom, }, @@ -228,25 +228,23 @@ const vectorStyle = async ( style: StyleSpecification; layersVisibility: LayerVisibility[]; }> => { - let header = await file.getHeader(); - let metadata = (await file.getMetadata()) as PMTilesMetadata; + const header = await file.getHeader(); + const metadata = (await file.getMetadata()) as Metadata; let layers: LayerSpecification[] = []; let baseOpacity = 0.35; if (metadata.type !== "baselayer") { - layers = base_theme("basemap", "black"); + layers = baseTheme("basemap", "black"); baseOpacity = 0.9; } - var tilestats: any; - var vector_layers: LayerSpecification[]; - tilestats = metadata.tilestats; - vector_layers = metadata.vector_layers; + const tilestats = metadata.tilestats; + const vectorLayers = metadata.vector_layers; - if (vector_layers) { - for (let [i, layer] of vector_layers.entries()) { + if (vectorLayers) { + for (const [i, layer] of vectorLayers.entries()) { layers.push({ - id: layer.id + "_fill", + id: `${layer.id}_fill`, type: "fill", source: "source", "source-layer": layer.id, @@ -268,7 +266,7 @@ const vectorStyle = async ( filter: ["==", ["geometry-type"], "Polygon"], }); layers.push({ - id: layer.id + "_stroke", + id: `${layer.id}_stroke`, type: "line", source: "source", "source-layer": layer.id, @@ -284,7 +282,7 @@ const vectorStyle = async ( filter: ["==", ["geometry-type"], "LineString"], }); layers.push({ - id: layer.id + "_point", + id: `${layer.id}_point`, type: "circle", source: "source", "source-layer": layer.id, @@ -315,7 +313,7 @@ const vectorStyle = async ( sources: { source: { type: "vector", - tiles: ["pmtiles://" + file.source.getKey() + "/{z}/{x}/{y}"], + tiles: [`pmtiles://${file.source.getKey()}/{z}/{x}/{y}`], minzoom: header.minZoom, maxzoom: header.maxZoom, bounds: bounds, @@ -331,7 +329,7 @@ const vectorStyle = async ( glyphs: "https://cdn.protomaps.com/fonts/pbf/{fontstack}/{range}.pbf", layers: layers, }, - layersVisibility: vector_layers.map((l: LayerSpecification) => ({ + layersVisibility: vectorLayers.map((l: LayerSpecification) => ({ id: l.id, visible: true, })), @@ -339,11 +337,13 @@ const vectorStyle = async ( }; function MaplibreMap(props: { file: PMTiles; mapHashPassed: boolean }) { - let mapContainerRef = useRef(null); - let [hamburgerOpen, setHamburgerOpen] = useState(true); - let [showAttributes, setShowAttributes] = useState(false); - let [showTileBoundaries, setShowTileBoundaries] = useState(false); - let [layersVisibility, setLayersVisibility] = useState([]); + const mapContainerRef = useRef(null); + const [hamburgerOpen, setHamburgerOpen] = useState(true); + const [showAttributes, setShowAttributes] = useState(false); + const [showTileBoundaries, setShowTileBoundaries] = useState(false); + const [layersVisibility, setLayersVisibility] = useState( + [] + ); const mapRef = useRef(null); const hoveredFeaturesRef = useRef>(new Set()); @@ -383,7 +383,7 @@ function MaplibreMap(props: { file: PMTiles; mapHashPassed: boolean }) { }; useEffect(() => { - let protocol = new Protocol(); + const protocol = new Protocol(); maplibregl.addProtocol("pmtiles", protocol.tile); protocol.add(props.file); // this is necessary for non-HTTP sources @@ -423,7 +423,7 @@ function MaplibreMap(props: { file: PMTiles; mapHashPassed: boolean }) { const { x, y } = e.point; const r = 2; // radius around the point - var features = map.queryRenderedFeatures([ + let features = map.queryRenderedFeatures([ [x - r, y - r], [x + r, y + r], ]); @@ -436,7 +436,9 @@ function MaplibreMap(props: { file: PMTiles; mapHashPassed: boolean }) { hoveredFeatures.add(feature); } - let content = renderToString(); + const content = renderToString( + + ); if (!features.length) { popup.remove(); } else { @@ -452,10 +454,10 @@ function MaplibreMap(props: { file: PMTiles; mapHashPassed: boolean }) { }, []); useEffect(() => { - let initStyle = async () => { + const initStyle = async () => { if (mapRef.current) { - let map = mapRef.current; - let header = await props.file.getHeader(); + const map = mapRef.current; + const header = await props.file.getHeader(); if (!props.mapHashPassed) { // the map hash was not passed, so auto-detect the initial viewport based on metadata map.fitBounds( @@ -471,12 +473,12 @@ function MaplibreMap(props: { file: PMTiles; mapHashPassed: boolean }) { if ( header.tileType === TileType.Png || header.tileType === TileType.Webp || - header.tileType == TileType.Jpeg + header.tileType === TileType.Jpeg ) { - let style = await rasterStyle(props.file); + const style = await rasterStyle(props.file); map.setStyle(style); } else { - let { style, layersVisibility } = await vectorStyle(props.file); + const { style, layersVisibility } = await vectorStyle(props.file); map.setStyle(style); setLayersVisibility(layersVisibility); } @@ -488,7 +490,7 @@ function MaplibreMap(props: { file: PMTiles; mapHashPassed: boolean }) { return ( -
+
menu {hamburgerOpen ? ( diff --git a/app/src/Metadata.tsx b/app/src/Metadata.tsx index 2fb91091..6eec3e68 100644 --- a/app/src/Metadata.tsx +++ b/app/src/Metadata.tsx @@ -1,7 +1,7 @@ -import { useState, useEffect } from "react"; -import { PMTiles, Header } from "../../js/index"; -import { styled } from "./stitches.config"; import { JsonViewer } from "@textea/json-viewer"; +import { useEffect, useState } from "react"; +import { Header, PMTiles } from "../../js/index"; +import { styled } from "./stitches.config"; const Padded = styled("div", { padding: "2rem", @@ -13,11 +13,11 @@ const Heading = styled("div", { }); function Metadata(props: { file: PMTiles }) { - let [metadata, setMetadata] = useState(); - let [header, setHeader] = useState
(null); + const [metadata, setMetadata] = useState(); + const [header, setHeader] = useState
(null); useEffect(() => { - let pmtiles = props.file; + const pmtiles = props.file; const fetchData = async () => { setMetadata(await pmtiles.getMetadata()); setHeader(await pmtiles.getHeader()); diff --git a/app/src/Start.tsx b/app/src/Start.tsx index 9d16b652..d19e700c 100644 --- a/app/src/Start.tsx +++ b/app/src/Start.tsx @@ -1,8 +1,7 @@ -import { useState, Dispatch, SetStateAction, useCallback } from "react"; -import maplibregl from "maplibre-gl"; -import { PMTiles, FileSource } from "../../js/index"; -import { styled } from "./stitches.config"; +import { Dispatch, SetStateAction, useCallback, useState } from "react"; import { useDropzone } from "react-dropzone"; +import { FileSource, PMTiles } from "../../js/index"; +import { styled } from "./stitches.config"; import * as LabelPrimitive from "@radix-ui/react-label"; @@ -13,7 +12,7 @@ const Input = styled("input", { justifyContent: "center", fontSize: "$3", fontFamily: "$sans", - "&:focus": { boxShadow: `0 0 0 1px black` }, + "&:focus": { boxShadow: "0 0 0 1px black" }, width: "100%", border: "1px solid $white", padding: "$1", @@ -115,8 +114,8 @@ function Start(props: { onDrop, }); - let [remoteUrl, setRemoteUrl] = useState(""); - let [selectedExample, setSelectedExample] = useState(1); + const [remoteUrl, setRemoteUrl] = useState(""); + const [selectedExample, setSelectedExample] = useState(1); const onRemoteUrlChangeHandler = ( event: React.ChangeEvent @@ -142,7 +141,7 @@ function Start(props: { id="remoteUrl" placeholder="https://example.com/my_archive.pmtiles" onChange={onRemoteUrlChangeHandler} - > + /> diff --git a/app/src/main.tsx b/app/src/main.tsx index f46c379c..920fd77b 100644 --- a/app/src/main.tsx +++ b/app/src/main.tsx @@ -1,9 +1,12 @@ import React from "react"; -import ReactDOM from "react-dom/client"; +import reactDom from "react-dom/client"; import App from "./App"; -ReactDOM.createRoot(document.getElementById("root")!).render( - - - -); +const root = document.getElementById("root"); +if (root) { + reactDom.createRoot(root).render( + + + + ); +}