mirror of
https://github.com/protomaps/PMTiles.git
synced 2026-02-04 10:51:07 +00:00
Add biome linter and configuration to /app viewer.
This commit is contained in:
20
app/biome.json
Normal file
20
app/biome.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"javascript": {
|
||||||
|
"formatter": {
|
||||||
|
"trailingComma": "es5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"formatter": {
|
||||||
|
"indentStyle": "space"
|
||||||
|
},
|
||||||
|
"linter": {
|
||||||
|
"rules": {
|
||||||
|
"style": {
|
||||||
|
"useNamingConvention": {}
|
||||||
|
},
|
||||||
|
"nursery": {
|
||||||
|
"noUnusedImports": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
156
app/package-lock.json
generated
156
app/package-lock.json
generated
@@ -28,6 +28,7 @@
|
|||||||
"react-use": "^17.4.0"
|
"react-use": "^17.4.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@biomejs/biome": "^1.5.3",
|
||||||
"@maplibre/maplibre-gl-style-spec": "^19.3.1",
|
"@maplibre/maplibre-gl-style-spec": "^19.3.1",
|
||||||
"@types/d3-path": "^3.0.0",
|
"@types/d3-path": "^3.0.0",
|
||||||
"@types/d3-scale-chromatic": "^3.0.0",
|
"@types/d3-scale-chromatic": "^3.0.0",
|
||||||
@@ -448,6 +449,161 @@
|
|||||||
"node": ">=6.9.0"
|
"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": {
|
"node_modules/@emotion/babel-plugin": {
|
||||||
"version": "11.10.5",
|
"version": "11.10.5",
|
||||||
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.5.tgz",
|
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.5.tgz",
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
"react-use": "^17.4.0"
|
"react-use": "^17.4.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@biomejs/biome": "^1.5.3",
|
||||||
"@maplibre/maplibre-gl-style-spec": "^19.3.1",
|
"@maplibre/maplibre-gl-style-spec": "^19.3.1",
|
||||||
"@types/d3-path": "^3.0.0",
|
"@types/d3-path": "^3.0.0",
|
||||||
"@types/d3-scale-chromatic": "^3.0.0",
|
"@types/d3-scale-chromatic": "^3.0.0",
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
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 * as DialogPrimitive from "@radix-ui/react-dialog";
|
||||||
|
import { GitHubLogoIcon } from "@radix-ui/react-icons";
|
||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import { PMTiles } from "../../js/index";
|
||||||
|
import { globalStyles, styled } from "./stitches.config";
|
||||||
|
|
||||||
import Start from "./Start";
|
|
||||||
import Loader from "./Loader";
|
import Loader from "./Loader";
|
||||||
|
import Start from "./Start";
|
||||||
|
|
||||||
const Header = styled("div", {
|
const Header = styled("div", {
|
||||||
height: "$4",
|
height: "$4",
|
||||||
@@ -57,15 +57,15 @@ const GIT_SHA = (import.meta.env.VITE_GIT_SHA || "").substr(0, 8);
|
|||||||
function App() {
|
function App() {
|
||||||
globalStyles();
|
globalStyles();
|
||||||
|
|
||||||
let [errorDisplay, setErrorDisplay] = useState<string | undefined>();
|
const [errorDisplay, setErrorDisplay] = useState<string | undefined>();
|
||||||
let [file, setFile] = useState<PMTiles | undefined>();
|
const [file, setFile] = useState<PMTiles | undefined>();
|
||||||
let [mapHashPassed, setMapHashPassed] = useState<boolean>(false);
|
const [mapHashPassed, setMapHashPassed] = useState<boolean>(false);
|
||||||
|
|
||||||
// initial load
|
// initial load
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadUrl = new URLSearchParams(location.search).get("url");
|
const loadUrl = new URLSearchParams(location.search).get("url");
|
||||||
if (loadUrl) {
|
if (loadUrl) {
|
||||||
let initialValue = new PMTiles(loadUrl);
|
const initialValue = new PMTiles(loadUrl);
|
||||||
setFile(initialValue);
|
setFile(initialValue);
|
||||||
}
|
}
|
||||||
if (location.hash.includes("map")) {
|
if (location.hash.includes("map")) {
|
||||||
@@ -84,7 +84,7 @@ function App() {
|
|||||||
// maintaining URL state
|
// maintaining URL state
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const url = new URL(window.location.href);
|
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());
|
url.searchParams.set("url", file.source.getKey());
|
||||||
history.pushState(null, "", url.toString());
|
history.pushState(null, "", url.toString());
|
||||||
} else {
|
} else {
|
||||||
@@ -93,7 +93,7 @@ function App() {
|
|||||||
}
|
}
|
||||||
}, [file]);
|
}, [file]);
|
||||||
|
|
||||||
let clear = (event: React.MouseEvent<HTMLAnchorElement>) => {
|
const clear = (event: React.MouseEvent<HTMLAnchorElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
setFile(undefined);
|
setFile(undefined);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
import { useState, useEffect, useRef, Dispatch, SetStateAction } from "react";
|
import { VectorTile } from "@mapbox/vector-tile";
|
||||||
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 { path } from "d3-path";
|
import { path } from "d3-path";
|
||||||
import { schemeSet3 } from "d3-scale-chromatic";
|
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 { 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", {
|
const TableContainer = styled("div", {
|
||||||
height: "calc(100vh - $4 - $4)",
|
height: "calc(100vh - $4 - $4)",
|
||||||
@@ -15,7 +14,7 @@ const TableContainer = styled("div", {
|
|||||||
width: "calc(100%/3)",
|
width: "calc(100%/3)",
|
||||||
});
|
});
|
||||||
|
|
||||||
const SVGContainer = styled("div", {
|
const SvgContainer = styled("div", {
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: "calc(100vh - $4 - $4)",
|
height: "calc(100vh - $4 - $4)",
|
||||||
});
|
});
|
||||||
@@ -45,7 +44,7 @@ const TileRow = (props: {
|
|||||||
entry: Entry;
|
entry: Entry;
|
||||||
setSelectedEntry: (val: Entry | null) => void;
|
setSelectedEntry: (val: Entry | null) => void;
|
||||||
}) => {
|
}) => {
|
||||||
let [z, x, y] = tileIdToZxy(props.entry.tileId);
|
const [z, x, y] = tileIdToZxy(props.entry.tileId);
|
||||||
return (
|
return (
|
||||||
<TableRow
|
<TableRow
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -59,7 +58,7 @@ const TileRow = (props: {
|
|||||||
<td>{props.entry.offset}</td>
|
<td>{props.entry.offset}</td>
|
||||||
<td>{props.entry.length}</td>
|
<td>{props.entry.length}</td>
|
||||||
<td>
|
<td>
|
||||||
{props.entry.runLength == 0
|
{props.entry.runLength === 0
|
||||||
? "directory"
|
? "directory"
|
||||||
: `tile(${props.entry.runLength})`}
|
: `tile(${props.entry.runLength})`}
|
||||||
</td>
|
</td>
|
||||||
@@ -80,7 +79,7 @@ interface Feature {
|
|||||||
properties: any;
|
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 === "earth") return -4;
|
||||||
if (a.name === "water") return -3;
|
if (a.name === "water") return -3;
|
||||||
if (a.name === "natural") return -2;
|
if (a.name === "natural") return -2;
|
||||||
@@ -93,7 +92,7 @@ const FeatureSvg = (props: {
|
|||||||
feature: Feature;
|
feature: Feature;
|
||||||
setSelectedFeature: Dispatch<SetStateAction<Feature | null>>;
|
setSelectedFeature: Dispatch<SetStateAction<Feature | null>>;
|
||||||
}) => {
|
}) => {
|
||||||
let [highlighted, setHighlighted] = useState(false);
|
const [highlighted, setHighlighted] = useState(false);
|
||||||
let fill = "none";
|
let fill = "none";
|
||||||
let stroke = "";
|
let stroke = "";
|
||||||
|
|
||||||
@@ -103,15 +102,15 @@ const FeatureSvg = (props: {
|
|||||||
stroke = highlighted ? "white" : "currentColor";
|
stroke = highlighted ? "white" : "currentColor";
|
||||||
}
|
}
|
||||||
|
|
||||||
let mouseOver = () => {
|
const mouseOver = () => {
|
||||||
setHighlighted(true);
|
setHighlighted(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
let mouseOut = () => {
|
const mouseOut = () => {
|
||||||
setHighlighted(false);
|
setHighlighted(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
let mouseDown = () => {
|
const mouseDown = () => {
|
||||||
props.setSelectedFeature(props.feature);
|
props.setSelectedFeature(props.feature);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -125,7 +124,7 @@ const FeatureSvg = (props: {
|
|||||||
onMouseOver={mouseOver}
|
onMouseOver={mouseOver}
|
||||||
onMouseOut={mouseOut}
|
onMouseOut={mouseOut}
|
||||||
onMouseDown={mouseDown}
|
onMouseDown={mouseDown}
|
||||||
></path>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -134,12 +133,12 @@ const LayerSvg = (props: {
|
|||||||
color: string;
|
color: string;
|
||||||
setSelectedFeature: Dispatch<SetStateAction<Feature | null>>;
|
setSelectedFeature: Dispatch<SetStateAction<Feature | null>>;
|
||||||
}) => {
|
}) => {
|
||||||
let elems = props.layer.features.map((f, i) => (
|
const elems = props.layer.features.map((f, i) => (
|
||||||
<FeatureSvg
|
<FeatureSvg
|
||||||
key={i}
|
key={i}
|
||||||
feature={f}
|
feature={f}
|
||||||
setSelectedFeature={props.setSelectedFeature}
|
setSelectedFeature={props.setSelectedFeature}
|
||||||
></FeatureSvg>
|
/>
|
||||||
));
|
));
|
||||||
return <g color={props.color}>{elems}</g>;
|
return <g color={props.color}>{elems}</g>;
|
||||||
};
|
};
|
||||||
@@ -153,8 +152,8 @@ const StyledFeatureProperties = styled("div", {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const FeatureProperties = (props: { feature: Feature }) => {
|
const FeatureProperties = (props: { feature: Feature }) => {
|
||||||
let tmp: [string, string][] = [];
|
const tmp: [string, string][] = [];
|
||||||
for (var key in props.feature.properties) {
|
for (const key in props.feature.properties) {
|
||||||
tmp.push([key, props.feature.properties[key]]);
|
tmp.push([key, props.feature.properties[key]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,44 +179,44 @@ const VectorPreview = (props: {
|
|||||||
entry: Entry;
|
entry: Entry;
|
||||||
tileType: TileType;
|
tileType: TileType;
|
||||||
}) => {
|
}) => {
|
||||||
let [layers, setLayers] = useState<Layer[]>([]);
|
const [layers, setLayers] = useState<Layer[]>([]);
|
||||||
let [maxExtent, setMaxExtent] = useState<number>(0);
|
const [maxExtent, setMaxExtent] = useState<number>(0);
|
||||||
let [selectedFeature, setSelectedFeature] = useState<Feature | null>(null);
|
const [selectedFeature, setSelectedFeature] = useState<Feature | null>(null);
|
||||||
const Viewer = useRef<UncontrolledReactSVGPanZoom>(null);
|
const viewer = useRef<UncontrolledReactSVGPanZoom>(null);
|
||||||
const [ref, { width, height }] = useMeasure<HTMLDivElement>();
|
const [ref, { width, height }] = useMeasure<HTMLDivElement>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
Viewer.current!.zoomOnViewerCenter(0.1);
|
viewer.current?.zoomOnViewerCenter(0.1);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let fn = async (entry: Entry) => {
|
const fn = async (entry: Entry) => {
|
||||||
let [z, x, y] = tileIdToZxy(entry.tileId);
|
const [z, x, y] = tileIdToZxy(entry.tileId);
|
||||||
let resp = await props.file.getZxy(z, x, y);
|
const resp = await props.file.getZxy(z, x, y);
|
||||||
|
|
||||||
let tile = new VectorTile(new Protobuf(new Uint8Array(resp!.data)));
|
const tile = new VectorTile(new Protobuf(new Uint8Array(resp!.data)));
|
||||||
let newLayers = [];
|
const newLayers = [];
|
||||||
let max_extent = 0;
|
let maxExtent = 0;
|
||||||
for (let [name, layer] of Object.entries(tile.layers)) {
|
for (const [name, layer] of Object.entries(tile.layers)) {
|
||||||
if (layer.extent > max_extent) {
|
if (layer.extent > maxExtent) {
|
||||||
max_extent = layer.extent;
|
maxExtent = layer.extent;
|
||||||
}
|
}
|
||||||
let features: Feature[] = [];
|
const features: Feature[] = [];
|
||||||
for (var i = 0; i < layer.length; i++) {
|
for (let i = 0; i < layer.length; i++) {
|
||||||
let feature = layer.feature(i);
|
const feature = layer.feature(i);
|
||||||
let p = path();
|
const p = path();
|
||||||
let geom = feature.loadGeometry();
|
const geom = feature.loadGeometry();
|
||||||
|
|
||||||
if (feature.type === 1) {
|
if (feature.type === 1) {
|
||||||
for (let ring of geom) {
|
for (const ring of geom) {
|
||||||
for (let pt of ring) {
|
for (const pt of ring) {
|
||||||
p.arc(pt.x, pt.y, 20, 0, 2 * Math.PI);
|
p.arc(pt.x, pt.y, 20, 0, 2 * Math.PI);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (let ring of geom) {
|
for (const ring of geom) {
|
||||||
p.moveTo(ring[0].x, ring[0].y);
|
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);
|
p.lineTo(ring[j].x, ring[j].y);
|
||||||
}
|
}
|
||||||
if (feature.type === 3) {
|
if (feature.type === 3) {
|
||||||
@@ -235,7 +234,7 @@ const VectorPreview = (props: {
|
|||||||
}
|
}
|
||||||
newLayers.push({ features: features, name: name });
|
newLayers.push({ features: features, name: name });
|
||||||
}
|
}
|
||||||
setMaxExtent(max_extent);
|
setMaxExtent(maxExtent);
|
||||||
newLayers.sort(smartCompare);
|
newLayers.sort(smartCompare);
|
||||||
setLayers(newLayers);
|
setLayers(newLayers);
|
||||||
};
|
};
|
||||||
@@ -245,19 +244,19 @@ const VectorPreview = (props: {
|
|||||||
}
|
}
|
||||||
}, [props.entry]);
|
}, [props.entry]);
|
||||||
|
|
||||||
let elems = layers.map((l, i) => (
|
const elems = layers.map((l, i) => (
|
||||||
<LayerSvg
|
<LayerSvg
|
||||||
key={i}
|
key={i}
|
||||||
layer={l}
|
layer={l}
|
||||||
color={schemeSet3[i % 12]}
|
color={schemeSet3[i % 12]}
|
||||||
setSelectedFeature={setSelectedFeature}
|
setSelectedFeature={setSelectedFeature}
|
||||||
></LayerSvg>
|
/>
|
||||||
));
|
));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SVGContainer ref={ref}>
|
<SvgContainer ref={ref}>
|
||||||
<UncontrolledReactSVGPanZoom
|
<UncontrolledReactSVGPanZoom
|
||||||
ref={Viewer}
|
ref={viewer}
|
||||||
width={width}
|
width={width}
|
||||||
height={height}
|
height={height}
|
||||||
detectAutoPan={false}
|
detectAutoPan={false}
|
||||||
@@ -268,20 +267,20 @@ const VectorPreview = (props: {
|
|||||||
<svg viewBox="0 0 4096 4096">{elems}</svg>
|
<svg viewBox="0 0 4096 4096">{elems}</svg>
|
||||||
</UncontrolledReactSVGPanZoom>
|
</UncontrolledReactSVGPanZoom>
|
||||||
{selectedFeature ? <FeatureProperties feature={selectedFeature} /> : null}
|
{selectedFeature ? <FeatureProperties feature={selectedFeature} /> : null}
|
||||||
</SVGContainer>
|
</SvgContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const RasterPreview = (props: { file: PMTiles; entry: Entry }) => {
|
const RasterPreview = (props: { file: PMTiles; entry: Entry }) => {
|
||||||
let [imgSrc, setImageSrc] = useState<string>("");
|
const [imgSrc, setImageSrc] = useState<string>("");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let fn = async (entry: Entry) => {
|
const fn = async (entry: Entry) => {
|
||||||
// TODO 0,0,0 is broken
|
// TODO 0,0,0 is broken
|
||||||
let [z, x, y] = tileIdToZxy(entry.tileId);
|
const [z, x, y] = tileIdToZxy(entry.tileId);
|
||||||
let resp = await props.file.getZxy(z, x, y);
|
const resp = await props.file.getZxy(z, x, y);
|
||||||
let blob = new Blob([resp!.data]);
|
const blob = new Blob([resp!.data]);
|
||||||
var imageUrl = window.URL.createObjectURL(blob);
|
const imageUrl = window.URL.createObjectURL(blob);
|
||||||
setImageSrc(imageUrl);
|
setImageSrc(imageUrl);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -290,12 +289,12 @@ const RasterPreview = (props: { file: PMTiles; entry: Entry }) => {
|
|||||||
}
|
}
|
||||||
}, [props.entry]);
|
}, [props.entry]);
|
||||||
|
|
||||||
return <img src={imgSrc}></img>;
|
return <img src={imgSrc} alt="raster tile" />;
|
||||||
};
|
};
|
||||||
|
|
||||||
function getHashString(entry: Entry) {
|
function getHashString(entry: Entry) {
|
||||||
const [z, x, y] = tileIdToZxy(entry.tileId);
|
const [z, x, y] = tileIdToZxy(entry.tileId);
|
||||||
let hash = `${z}/${x}/${y}`;
|
const hash = `${z}/${x}/${y}`;
|
||||||
|
|
||||||
const hashName = "inspector";
|
const hashName = "inspector";
|
||||||
let found = false;
|
let found = false;
|
||||||
@@ -318,9 +317,9 @@ function getHashString(entry: Entry) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Inspector(props: { file: PMTiles }) {
|
function Inspector(props: { file: PMTiles }) {
|
||||||
let [entryRows, setEntryRows] = useState<Entry[]>([]);
|
const [entryRows, setEntryRows] = useState<Entry[]>([]);
|
||||||
let [selectedEntry, setSelectedEntryRaw] = useState<Entry | null>(null);
|
const [selectedEntry, setSelectedEntryRaw] = useState<Entry | null>(null);
|
||||||
let [header, setHeader] = useState<Header | null>(null);
|
const [header, setHeader] = useState<Header | null>(null);
|
||||||
|
|
||||||
function setSelectedEntry(val: Entry | null) {
|
function setSelectedEntry(val: Entry | null) {
|
||||||
if (val && val.runLength > 0) {
|
if (val && val.runLength > 0) {
|
||||||
@@ -330,13 +329,13 @@ function Inspector(props: { file: PMTiles }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let fn = async () => {
|
const fn = async () => {
|
||||||
let header = await props.file.getHeader();
|
const header = await props.file.getHeader();
|
||||||
setHeader(header);
|
setHeader(header);
|
||||||
if (header.specVersion < 3) {
|
if (header.specVersion < 3) {
|
||||||
setEntryRows([]);
|
setEntryRows([]);
|
||||||
} else if (selectedEntry !== null && selectedEntry.runLength === 0) {
|
} else if (selectedEntry !== null && selectedEntry.runLength === 0) {
|
||||||
let entries = await props.file.cache.getDirectory(
|
const entries = await props.file.cache.getDirectory(
|
||||||
props.file.source,
|
props.file.source,
|
||||||
header.leafDirectoryOffset + selectedEntry.offset,
|
header.leafDirectoryOffset + selectedEntry.offset,
|
||||||
selectedEntry.length,
|
selectedEntry.length,
|
||||||
@@ -344,7 +343,7 @@ function Inspector(props: { file: PMTiles }) {
|
|||||||
);
|
);
|
||||||
setEntryRows(entries);
|
setEntryRows(entries);
|
||||||
} else if (selectedEntry === null) {
|
} else if (selectedEntry === null) {
|
||||||
let entries = await props.file.cache.getDirectory(
|
const entries = await props.file.cache.getDirectory(
|
||||||
props.file.source,
|
props.file.source,
|
||||||
header.rootDirectoryOffset,
|
header.rootDirectoryOffset,
|
||||||
header.rootDirectoryLength,
|
header.rootDirectoryLength,
|
||||||
@@ -357,11 +356,11 @@ function Inspector(props: { file: PMTiles }) {
|
|||||||
fn();
|
fn();
|
||||||
}, [props.file, selectedEntry]);
|
}, [props.file, selectedEntry]);
|
||||||
|
|
||||||
let rows = entryRows.map((e, i) => (
|
const rows = entryRows.map((e, i) => (
|
||||||
<TileRow key={i} entry={e} setSelectedEntry={setSelectedEntry}></TileRow>
|
<TileRow key={i} entry={e} setSelectedEntry={setSelectedEntry} />
|
||||||
));
|
));
|
||||||
|
|
||||||
let tilePreview = <div></div>;
|
let tilePreview = <div />;
|
||||||
if (selectedEntry && header?.tileType) {
|
if (selectedEntry && header?.tileType) {
|
||||||
if (selectedEntry.runLength === 0) {
|
if (selectedEntry.runLength === 0) {
|
||||||
// do nothing
|
// do nothing
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useState } from "react";
|
||||||
import { PMTiles } from "../../js/index";
|
import { PMTiles } from "../../js/index";
|
||||||
import { styled } from "./stitches.config";
|
import { styled } from "./stitches.config";
|
||||||
|
|
||||||
@@ -6,7 +6,7 @@ import Inspector from "./Inspector";
|
|||||||
import MaplibreMap from "./MaplibreMap";
|
import MaplibreMap from "./MaplibreMap";
|
||||||
import Metadata from "./Metadata";
|
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";
|
import * as ToolbarPrimitive from "@radix-ui/react-toolbar";
|
||||||
|
|
||||||
const StyledToolbar = styled(ToolbarPrimitive.Root, {
|
const StyledToolbar = styled(ToolbarPrimitive.Root, {
|
||||||
@@ -28,7 +28,7 @@ const itemStyles = {
|
|||||||
fontSize: "$2",
|
fontSize: "$2",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
"&:hover": { backgroundColor: "$hover", color: "$white" },
|
"&: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(
|
const StyledLink = styled(
|
||||||
@@ -71,9 +71,9 @@ const ToolbarToggleGroup = StyledToggleGroup;
|
|||||||
const ToolbarToggleItem = StyledToggleItem;
|
const ToolbarToggleItem = StyledToggleItem;
|
||||||
|
|
||||||
function Loader(props: { file: PMTiles; mapHashPassed: boolean }) {
|
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") {
|
if (tab === "maplibre") {
|
||||||
view = (
|
view = (
|
||||||
<MaplibreMap file={props.file} mapHashPassed={props.mapHashPassed} />
|
<MaplibreMap file={props.file} mapHashPassed={props.mapHashPassed} />
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import React, { useState, useEffect, useRef } from "react";
|
import {
|
||||||
import { renderToString } from "react-dom/server";
|
LayerSpecification,
|
||||||
import { PMTiles, TileType } from "../../js/index";
|
StyleSpecification,
|
||||||
import { Protocol } from "../../js/adapters";
|
} from "@maplibre/maplibre-gl-style-spec";
|
||||||
import { styled } from "./stitches.config";
|
import { schemeSet3 } from "d3-scale-chromatic";
|
||||||
import maplibregl from "maplibre-gl";
|
import maplibregl from "maplibre-gl";
|
||||||
import { MapGeoJSONFeature } from "maplibre-gl";
|
import { MapGeoJSONFeature } from "maplibre-gl";
|
||||||
import "maplibre-gl/dist/maplibre-gl.css";
|
import "maplibre-gl/dist/maplibre-gl.css";
|
||||||
import { schemeSet3 } from "d3-scale-chromatic";
|
import baseTheme from "protomaps-themes-base";
|
||||||
import base_theme from "protomaps-themes-base";
|
import React, { useState, useEffect, useRef } from "react";
|
||||||
import {
|
import { renderToString } from "react-dom/server";
|
||||||
StyleSpecification,
|
import { Protocol } from "../../js/adapters";
|
||||||
LayerSpecification,
|
import { PMTiles, TileType } from "../../js/index";
|
||||||
} from "@maplibre/maplibre-gl-style-spec";
|
import { styled } from "./stitches.config";
|
||||||
|
|
||||||
const INITIAL_ZOOM = 0;
|
const INITIAL_ZOOM = 0;
|
||||||
const INITIAL_LNG = 0;
|
const INITIAL_LNG = 0;
|
||||||
@@ -102,7 +102,7 @@ interface LayerVisibility {
|
|||||||
visible: boolean;
|
visible: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PMTilesMetadata {
|
interface Metadata {
|
||||||
name?: string;
|
name?: string;
|
||||||
type?: string;
|
type?: string;
|
||||||
tilestats?: unknown;
|
tilestats?: unknown;
|
||||||
@@ -187,12 +187,12 @@ const LayersVisibilityController = (props: {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const rasterStyle = async (file: PMTiles): Promise<StyleSpecification> => {
|
const rasterStyle = async (file: PMTiles): Promise<StyleSpecification> => {
|
||||||
let header = await file.getHeader();
|
const header = await file.getHeader();
|
||||||
let metadata = (await file.getMetadata()) as PMTilesMetadata;
|
const metadata = (await file.getMetadata()) as Metadata;
|
||||||
let layers: LayerSpecification[] = [];
|
let layers: LayerSpecification[] = [];
|
||||||
|
|
||||||
if (metadata.type !== "baselayer") {
|
if (metadata.type !== "baselayer") {
|
||||||
layers = base_theme("basemap", "black");
|
layers = baseTheme("basemap", "black");
|
||||||
}
|
}
|
||||||
|
|
||||||
layers.push({
|
layers.push({
|
||||||
@@ -206,7 +206,7 @@ const rasterStyle = async (file: PMTiles): Promise<StyleSpecification> => {
|
|||||||
sources: {
|
sources: {
|
||||||
source: {
|
source: {
|
||||||
type: "raster",
|
type: "raster",
|
||||||
tiles: ["pmtiles://" + file.source.getKey() + "/{z}/{x}/{y}"],
|
tiles: [`pmtiles://${file.source.getKey()}/{z}/{x}/{y}`],
|
||||||
minzoom: header.minZoom,
|
minzoom: header.minZoom,
|
||||||
maxzoom: header.maxZoom,
|
maxzoom: header.maxZoom,
|
||||||
},
|
},
|
||||||
@@ -228,25 +228,23 @@ const vectorStyle = async (
|
|||||||
style: StyleSpecification;
|
style: StyleSpecification;
|
||||||
layersVisibility: LayerVisibility[];
|
layersVisibility: LayerVisibility[];
|
||||||
}> => {
|
}> => {
|
||||||
let header = await file.getHeader();
|
const header = await file.getHeader();
|
||||||
let metadata = (await file.getMetadata()) as PMTilesMetadata;
|
const metadata = (await file.getMetadata()) as Metadata;
|
||||||
let layers: LayerSpecification[] = [];
|
let layers: LayerSpecification[] = [];
|
||||||
let baseOpacity = 0.35;
|
let baseOpacity = 0.35;
|
||||||
|
|
||||||
if (metadata.type !== "baselayer") {
|
if (metadata.type !== "baselayer") {
|
||||||
layers = base_theme("basemap", "black");
|
layers = baseTheme("basemap", "black");
|
||||||
baseOpacity = 0.9;
|
baseOpacity = 0.9;
|
||||||
}
|
}
|
||||||
|
|
||||||
var tilestats: any;
|
const tilestats = metadata.tilestats;
|
||||||
var vector_layers: LayerSpecification[];
|
const vectorLayers = metadata.vector_layers;
|
||||||
tilestats = metadata.tilestats;
|
|
||||||
vector_layers = metadata.vector_layers;
|
|
||||||
|
|
||||||
if (vector_layers) {
|
if (vectorLayers) {
|
||||||
for (let [i, layer] of vector_layers.entries()) {
|
for (const [i, layer] of vectorLayers.entries()) {
|
||||||
layers.push({
|
layers.push({
|
||||||
id: layer.id + "_fill",
|
id: `${layer.id}_fill`,
|
||||||
type: "fill",
|
type: "fill",
|
||||||
source: "source",
|
source: "source",
|
||||||
"source-layer": layer.id,
|
"source-layer": layer.id,
|
||||||
@@ -268,7 +266,7 @@ const vectorStyle = async (
|
|||||||
filter: ["==", ["geometry-type"], "Polygon"],
|
filter: ["==", ["geometry-type"], "Polygon"],
|
||||||
});
|
});
|
||||||
layers.push({
|
layers.push({
|
||||||
id: layer.id + "_stroke",
|
id: `${layer.id}_stroke`,
|
||||||
type: "line",
|
type: "line",
|
||||||
source: "source",
|
source: "source",
|
||||||
"source-layer": layer.id,
|
"source-layer": layer.id,
|
||||||
@@ -284,7 +282,7 @@ const vectorStyle = async (
|
|||||||
filter: ["==", ["geometry-type"], "LineString"],
|
filter: ["==", ["geometry-type"], "LineString"],
|
||||||
});
|
});
|
||||||
layers.push({
|
layers.push({
|
||||||
id: layer.id + "_point",
|
id: `${layer.id}_point`,
|
||||||
type: "circle",
|
type: "circle",
|
||||||
source: "source",
|
source: "source",
|
||||||
"source-layer": layer.id,
|
"source-layer": layer.id,
|
||||||
@@ -315,7 +313,7 @@ const vectorStyle = async (
|
|||||||
sources: {
|
sources: {
|
||||||
source: {
|
source: {
|
||||||
type: "vector",
|
type: "vector",
|
||||||
tiles: ["pmtiles://" + file.source.getKey() + "/{z}/{x}/{y}"],
|
tiles: [`pmtiles://${file.source.getKey()}/{z}/{x}/{y}`],
|
||||||
minzoom: header.minZoom,
|
minzoom: header.minZoom,
|
||||||
maxzoom: header.maxZoom,
|
maxzoom: header.maxZoom,
|
||||||
bounds: bounds,
|
bounds: bounds,
|
||||||
@@ -331,7 +329,7 @@ const vectorStyle = async (
|
|||||||
glyphs: "https://cdn.protomaps.com/fonts/pbf/{fontstack}/{range}.pbf",
|
glyphs: "https://cdn.protomaps.com/fonts/pbf/{fontstack}/{range}.pbf",
|
||||||
layers: layers,
|
layers: layers,
|
||||||
},
|
},
|
||||||
layersVisibility: vector_layers.map((l: LayerSpecification) => ({
|
layersVisibility: vectorLayers.map((l: LayerSpecification) => ({
|
||||||
id: l.id,
|
id: l.id,
|
||||||
visible: true,
|
visible: true,
|
||||||
})),
|
})),
|
||||||
@@ -339,11 +337,13 @@ const vectorStyle = async (
|
|||||||
};
|
};
|
||||||
|
|
||||||
function MaplibreMap(props: { file: PMTiles; mapHashPassed: boolean }) {
|
function MaplibreMap(props: { file: PMTiles; mapHashPassed: boolean }) {
|
||||||
let mapContainerRef = useRef<HTMLDivElement>(null);
|
const mapContainerRef = useRef<HTMLDivElement>(null);
|
||||||
let [hamburgerOpen, setHamburgerOpen] = useState<boolean>(true);
|
const [hamburgerOpen, setHamburgerOpen] = useState<boolean>(true);
|
||||||
let [showAttributes, setShowAttributes] = useState<boolean>(false);
|
const [showAttributes, setShowAttributes] = useState<boolean>(false);
|
||||||
let [showTileBoundaries, setShowTileBoundaries] = useState<boolean>(false);
|
const [showTileBoundaries, setShowTileBoundaries] = useState<boolean>(false);
|
||||||
let [layersVisibility, setLayersVisibility] = useState<LayerVisibility[]>([]);
|
const [layersVisibility, setLayersVisibility] = useState<LayerVisibility[]>(
|
||||||
|
[]
|
||||||
|
);
|
||||||
const mapRef = useRef<maplibregl.Map | null>(null);
|
const mapRef = useRef<maplibregl.Map | null>(null);
|
||||||
const hoveredFeaturesRef = useRef<Set<MapGeoJSONFeature>>(new Set());
|
const hoveredFeaturesRef = useRef<Set<MapGeoJSONFeature>>(new Set());
|
||||||
|
|
||||||
@@ -383,7 +383,7 @@ function MaplibreMap(props: { file: PMTiles; mapHashPassed: boolean }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let protocol = new Protocol();
|
const protocol = new Protocol();
|
||||||
maplibregl.addProtocol("pmtiles", protocol.tile);
|
maplibregl.addProtocol("pmtiles", protocol.tile);
|
||||||
protocol.add(props.file); // this is necessary for non-HTTP sources
|
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 { x, y } = e.point;
|
||||||
const r = 2; // radius around the point
|
const r = 2; // radius around the point
|
||||||
var features = map.queryRenderedFeatures([
|
let features = map.queryRenderedFeatures([
|
||||||
[x - r, y - r],
|
[x - r, y - r],
|
||||||
[x + r, y + r],
|
[x + r, y + r],
|
||||||
]);
|
]);
|
||||||
@@ -436,7 +436,9 @@ function MaplibreMap(props: { file: PMTiles; mapHashPassed: boolean }) {
|
|||||||
hoveredFeatures.add(feature);
|
hoveredFeatures.add(feature);
|
||||||
}
|
}
|
||||||
|
|
||||||
let content = renderToString(<FeaturesProperties features={features} />);
|
const content = renderToString(
|
||||||
|
<FeaturesProperties features={features} />
|
||||||
|
);
|
||||||
if (!features.length) {
|
if (!features.length) {
|
||||||
popup.remove();
|
popup.remove();
|
||||||
} else {
|
} else {
|
||||||
@@ -452,10 +454,10 @@ function MaplibreMap(props: { file: PMTiles; mapHashPassed: boolean }) {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let initStyle = async () => {
|
const initStyle = async () => {
|
||||||
if (mapRef.current) {
|
if (mapRef.current) {
|
||||||
let map = mapRef.current;
|
const map = mapRef.current;
|
||||||
let header = await props.file.getHeader();
|
const header = await props.file.getHeader();
|
||||||
if (!props.mapHashPassed) {
|
if (!props.mapHashPassed) {
|
||||||
// the map hash was not passed, so auto-detect the initial viewport based on metadata
|
// the map hash was not passed, so auto-detect the initial viewport based on metadata
|
||||||
map.fitBounds(
|
map.fitBounds(
|
||||||
@@ -471,12 +473,12 @@ function MaplibreMap(props: { file: PMTiles; mapHashPassed: boolean }) {
|
|||||||
if (
|
if (
|
||||||
header.tileType === TileType.Png ||
|
header.tileType === TileType.Png ||
|
||||||
header.tileType === TileType.Webp ||
|
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);
|
map.setStyle(style);
|
||||||
} else {
|
} else {
|
||||||
let { style, layersVisibility } = await vectorStyle(props.file);
|
const { style, layersVisibility } = await vectorStyle(props.file);
|
||||||
map.setStyle(style);
|
map.setStyle(style);
|
||||||
setLayersVisibility(layersVisibility);
|
setLayersVisibility(layersVisibility);
|
||||||
}
|
}
|
||||||
@@ -488,7 +490,7 @@ function MaplibreMap(props: { file: PMTiles; mapHashPassed: boolean }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<MapContainer ref={mapContainerRef}>
|
<MapContainer ref={mapContainerRef}>
|
||||||
<div ref={mapContainerRef}></div>
|
<div ref={mapContainerRef} />
|
||||||
<Hamburger onClick={toggleHamburger}>menu</Hamburger>
|
<Hamburger onClick={toggleHamburger}>menu</Hamburger>
|
||||||
{hamburgerOpen ? (
|
{hamburgerOpen ? (
|
||||||
<Options>
|
<Options>
|
||||||
|
|||||||
@@ -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 { 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", {
|
const Padded = styled("div", {
|
||||||
padding: "2rem",
|
padding: "2rem",
|
||||||
@@ -13,11 +13,11 @@ const Heading = styled("div", {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function Metadata(props: { file: PMTiles }) {
|
function Metadata(props: { file: PMTiles }) {
|
||||||
let [metadata, setMetadata] = useState<any>();
|
const [metadata, setMetadata] = useState<unknown>();
|
||||||
let [header, setHeader] = useState<Header | null>(null);
|
const [header, setHeader] = useState<Header | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let pmtiles = props.file;
|
const pmtiles = props.file;
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
setMetadata(await pmtiles.getMetadata());
|
setMetadata(await pmtiles.getMetadata());
|
||||||
setHeader(await pmtiles.getHeader());
|
setHeader(await pmtiles.getHeader());
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { useState, Dispatch, SetStateAction, useCallback } from "react";
|
import { Dispatch, SetStateAction, useCallback, useState } from "react";
|
||||||
import maplibregl from "maplibre-gl";
|
|
||||||
import { PMTiles, FileSource } from "../../js/index";
|
|
||||||
import { styled } from "./stitches.config";
|
|
||||||
import { useDropzone } from "react-dropzone";
|
import { useDropzone } from "react-dropzone";
|
||||||
|
import { FileSource, PMTiles } from "../../js/index";
|
||||||
|
import { styled } from "./stitches.config";
|
||||||
|
|
||||||
import * as LabelPrimitive from "@radix-ui/react-label";
|
import * as LabelPrimitive from "@radix-ui/react-label";
|
||||||
|
|
||||||
@@ -13,7 +12,7 @@ const Input = styled("input", {
|
|||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
fontSize: "$3",
|
fontSize: "$3",
|
||||||
fontFamily: "$sans",
|
fontFamily: "$sans",
|
||||||
"&:focus": { boxShadow: `0 0 0 1px black` },
|
"&:focus": { boxShadow: "0 0 0 1px black" },
|
||||||
width: "100%",
|
width: "100%",
|
||||||
border: "1px solid $white",
|
border: "1px solid $white",
|
||||||
padding: "$1",
|
padding: "$1",
|
||||||
@@ -115,8 +114,8 @@ function Start(props: {
|
|||||||
onDrop,
|
onDrop,
|
||||||
});
|
});
|
||||||
|
|
||||||
let [remoteUrl, setRemoteUrl] = useState<string>("");
|
const [remoteUrl, setRemoteUrl] = useState<string>("");
|
||||||
let [selectedExample, setSelectedExample] = useState<number | null>(1);
|
const [selectedExample, setSelectedExample] = useState<number | null>(1);
|
||||||
|
|
||||||
const onRemoteUrlChangeHandler = (
|
const onRemoteUrlChangeHandler = (
|
||||||
event: React.ChangeEvent<HTMLInputElement>
|
event: React.ChangeEvent<HTMLInputElement>
|
||||||
@@ -142,7 +141,7 @@ function Start(props: {
|
|||||||
id="remoteUrl"
|
id="remoteUrl"
|
||||||
placeholder="https://example.com/my_archive.pmtiles"
|
placeholder="https://example.com/my_archive.pmtiles"
|
||||||
onChange={onRemoteUrlChangeHandler}
|
onChange={onRemoteUrlChangeHandler}
|
||||||
></Input>
|
/>
|
||||||
<Button color="gray" onClick={onSubmit} disabled={!remoteUrl.trim()}>
|
<Button color="gray" onClick={onSubmit} disabled={!remoteUrl.trim()}>
|
||||||
Load URL
|
Load URL
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import ReactDOM from "react-dom/client";
|
import reactDom from "react-dom/client";
|
||||||
import App from "./App";
|
import App from "./App";
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
const root = document.getElementById("root");
|
||||||
|
if (root) {
|
||||||
|
reactDom.createRoot(root).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<App />
|
<App />
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user