mirror of
https://github.com/protomaps/PMTiles.git
synced 2026-02-04 19:01:08 +00:00
Add biome linter and configuration to /app viewer.
This commit is contained in:
@@ -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 (
|
||||
<TableRow
|
||||
onClick={() => {
|
||||
@@ -59,7 +58,7 @@ const TileRow = (props: {
|
||||
<td>{props.entry.offset}</td>
|
||||
<td>{props.entry.length}</td>
|
||||
<td>
|
||||
{props.entry.runLength == 0
|
||||
{props.entry.runLength === 0
|
||||
? "directory"
|
||||
: `tile(${props.entry.runLength})`}
|
||||
</td>
|
||||
@@ -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<SetStateAction<Feature | null>>;
|
||||
}) => {
|
||||
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}
|
||||
></path>
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -134,12 +133,12 @@ const LayerSvg = (props: {
|
||||
color: string;
|
||||
setSelectedFeature: Dispatch<SetStateAction<Feature | null>>;
|
||||
}) => {
|
||||
let elems = props.layer.features.map((f, i) => (
|
||||
const elems = props.layer.features.map((f, i) => (
|
||||
<FeatureSvg
|
||||
key={i}
|
||||
feature={f}
|
||||
setSelectedFeature={props.setSelectedFeature}
|
||||
></FeatureSvg>
|
||||
/>
|
||||
));
|
||||
return <g color={props.color}>{elems}</g>;
|
||||
};
|
||||
@@ -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<Layer[]>([]);
|
||||
let [maxExtent, setMaxExtent] = useState<number>(0);
|
||||
let [selectedFeature, setSelectedFeature] = useState<Feature | null>(null);
|
||||
const Viewer = useRef<UncontrolledReactSVGPanZoom>(null);
|
||||
const [layers, setLayers] = useState<Layer[]>([]);
|
||||
const [maxExtent, setMaxExtent] = useState<number>(0);
|
||||
const [selectedFeature, setSelectedFeature] = useState<Feature | null>(null);
|
||||
const viewer = useRef<UncontrolledReactSVGPanZoom>(null);
|
||||
const [ref, { width, height }] = useMeasure<HTMLDivElement>();
|
||||
|
||||
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);
|
||||
const fn = async (entry: Entry) => {
|
||||
const [z, x, y] = tileIdToZxy(entry.tileId);
|
||||
const 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 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) => (
|
||||
<LayerSvg
|
||||
key={i}
|
||||
layer={l}
|
||||
color={schemeSet3[i % 12]}
|
||||
setSelectedFeature={setSelectedFeature}
|
||||
></LayerSvg>
|
||||
/>
|
||||
));
|
||||
|
||||
return (
|
||||
<SVGContainer ref={ref}>
|
||||
<SvgContainer ref={ref}>
|
||||
<UncontrolledReactSVGPanZoom
|
||||
ref={Viewer}
|
||||
ref={viewer}
|
||||
width={width}
|
||||
height={height}
|
||||
detectAutoPan={false}
|
||||
@@ -268,20 +267,20 @@ const VectorPreview = (props: {
|
||||
<svg viewBox="0 0 4096 4096">{elems}</svg>
|
||||
</UncontrolledReactSVGPanZoom>
|
||||
{selectedFeature ? <FeatureProperties feature={selectedFeature} /> : null}
|
||||
</SVGContainer>
|
||||
</SvgContainer>
|
||||
);
|
||||
};
|
||||
|
||||
const RasterPreview = (props: { file: PMTiles; entry: Entry }) => {
|
||||
let [imgSrc, setImageSrc] = useState<string>("");
|
||||
const [imgSrc, setImageSrc] = useState<string>("");
|
||||
|
||||
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 <img src={imgSrc}></img>;
|
||||
return <img src={imgSrc} alt="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<Entry[]>([]);
|
||||
let [selectedEntry, setSelectedEntryRaw] = useState<Entry | null>(null);
|
||||
let [header, setHeader] = useState<Header | null>(null);
|
||||
const [entryRows, setEntryRows] = useState<Entry[]>([]);
|
||||
const [selectedEntry, setSelectedEntryRaw] = useState<Entry | null>(null);
|
||||
const [header, setHeader] = useState<Header | null>(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) => (
|
||||
<TileRow key={i} entry={e} setSelectedEntry={setSelectedEntry}></TileRow>
|
||||
const rows = entryRows.map((e, i) => (
|
||||
<TileRow key={i} entry={e} setSelectedEntry={setSelectedEntry} />
|
||||
));
|
||||
|
||||
let tilePreview = <div></div>;
|
||||
let tilePreview = <div />;
|
||||
if (selectedEntry && header?.tileType) {
|
||||
if (selectedEntry.runLength === 0) {
|
||||
// do nothing
|
||||
|
||||
Reference in New Issue
Block a user