inspector app has shared tile type detection

This commit is contained in:
Brandon Liu
2022-05-05 11:22:35 +08:00
parent ade6b7508e
commit e926399853
6 changed files with 129 additions and 47 deletions

View File

@@ -2,9 +2,9 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/src/favicon.svg" /> <link rel="icon" href="data:,">
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title> <title>PMTiles Viewer</title>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>

View File

@@ -2,15 +2,13 @@ import { useState, useEffect } from "react";
import { styled, globalStyles } from "./stitches.config"; import { styled, globalStyles } from "./stitches.config";
import Start from "./Start"; import Start from "./Start";
import Inspector from "./Inspector"; import Loader from "./Loader";
import LeafletMap from "./LeafletMap";
import MaplibreMap from "./MaplibreMap";
const Header = styled("div", { const Header = styled("div", {
height: "$4", height: "$4",
}); });
const GIT_SHA = import.meta.env.VITE_GIT_SHA.substr(0,8) const GIT_SHA = (import.meta.env.VITE_GIT_SHA || "").substr(0,8)
function App() { function App() {
globalStyles(); globalStyles();
@@ -30,7 +28,7 @@ function App() {
return ( return (
<div> <div>
<Header>pmtiles viewer | github | toggle | {GIT_SHA}</Header> <Header>pmtiles viewer | github | toggle | {GIT_SHA}</Header>
{file ? <MaplibreMap file={file} /> : <Start setFile={setFile} />} {file ? <Loader file={file} /> : <Start setFile={setFile} />}
</div> </div>
); );
} }

View File

@@ -2,7 +2,7 @@ import { useState } from "react";
import { PMTiles } from "../../js"; import { PMTiles } from "../../js";
import { styled } from "./stitches.config"; import { styled } from "./stitches.config";
function Inspector() { function Inspector(props:{file:string}) {
return ( return (
<> <>
foo foo

View File

@@ -8,7 +8,7 @@ const MapContainer = styled("div", {
height: "calc(100vh - $4)", height: "calc(100vh - $4)",
}); });
function LeafletMap(props:{file:string}) { function LeafletMap(props:{file:string, tileType: string | null}) {
const p = new PMTiles( const p = new PMTiles(
"https://protomaps-static.sfo3.digitaloceanspaces.com/osm_carto.pmtiles" "https://protomaps-static.sfo3.digitaloceanspaces.com/osm_carto.pmtiles"
); );

55
app/src/Loader.tsx Normal file
View File

@@ -0,0 +1,55 @@
import { useState, useEffect } from "react";
import { PMTiles } from "../../js";
import { styled } from "./stitches.config";
import Inspector from "./Inspector";
import LeafletMap from "./LeafletMap";
import MaplibreMap from "./MaplibreMap";
function Loader(props: { file: string }) {
let [tab, setTab] = useState("maplibre");
let [tileType, setTileType] = useState<string | null>(null);
let view;
if (tab === "leaflet") {
view = <LeafletMap file={props.file} tileType={tileType}/>;
} else if (tab === "maplibre") {
view = <MaplibreMap file={props.file} tileType={tileType}/>;
} else {
view = <Inspector file={props.file} />;
}
useEffect(() => {
let pmtiles = new PMTiles(props.file);
const fetchData = async () => {
let metadata = await pmtiles.metadata();
let resp = await fetch(props.file, {
headers: { Range: "bytes=512000-512003" },
});
let magic = new DataView(await resp.arrayBuffer());
let b0 = magic.getUint8(0);
let b1 = magic.getUint8(1);
let b2 = magic.getUint8(2);
let b3 = magic.getUint8(3);
if (b0 == 0x89 && b1 == 0x50 && b2 == 0x4e && b3 == 0x47) {
setTileType("png");
} else if (b0 == 0xff && b1 == 0xd8 && b2 == 0xff && b3 == 0xe0) {
setTileType("jpg")
} else if (b0 == 0x1f && b1 == 0x8b) {
setTileType("mvt.gz");
} else {
setTileType("mvt");
}
};
fetchData();
}, [props.file]);
return <>
<div>{props.file} | {tileType}</div>
{view}
</>;
}
export default Loader;

View File

@@ -1,58 +1,87 @@
import { useState, useEffect } from "react"; import { useState, useEffect, useRef } from "react";
import { PMTiles, ProtocolCache } from "../../js"; import { PMTiles, ProtocolCache } from "../../js";
import { styled } from "./stitches.config"; import { styled } from "./stitches.config";
import maplibregl from "maplibre-gl"; import maplibregl from "maplibre-gl";
import "maplibre-gl/dist/maplibre-gl.css" import "maplibre-gl/dist/maplibre-gl.css";
const MapContainer = styled("div", { const MapContainer = styled("div", {
height: "calc(100vh - $4)", height: "calc(100vh - $4)",
}); });
function MaplibreMap(props:{file:string}) { const rasterStyle = (file:string) => {
let cache = new ProtocolCache(); return {
maplibregl.addProtocol("pmtiles",cache.protocol); version: 8,
sources: {
var style = { source: {
"version": 8, type: "raster",
"sources": { tiles: ["pmtiles://" + file + "/{z}/{x}/{y}"],
"zcta": { maxzoom:4
"type": "vector",
"tiles": ["pmtiles://" + props.file + "/{z}/{x}/{y}"],
"maxzoom":7
}
}, },
"layers": [ },
{ layers: [
"id": "zcta_fill", {
"type": "fill", id: "raster",
"source":"zcta", type: "raster",
"source-layer":"zcta", source: "source"
"paint": { },
"fill-color":"white" ],
} };
}, };
{
"id": "zcta_stroke",
"type": "line",
"source":"zcta",
"source-layer":"zcta",
"paint": {
"line-color":"steelblue",
"line-width":0.5
}
}
]
}
const vectorStyle = (file:string) => {
return {
version: 8,
sources: {
source: {
type: "vector",
tiles: ["pmtiles://" + file + "/{z}/{x}/{y}"],
maxzoom: 7,
},
},
layers: [
{
id: "zcta_fill",
type: "fill",
source: "source",
"source-layer": "zcta",
paint: {
"fill-color": "white",
},
},
{
id: "zcta_stroke",
type: "line",
source: "source",
"source-layer": "zcta",
paint: {
"line-color": "steelblue",
"line-width": 0.5,
},
},
],
};
};
function MaplibreMap(props: { file: string, tileType: string | null }) {
let mapRef = useRef<HTMLDivElement>(null);
useEffect(() => { useEffect(() => {
const map = new maplibregl.Map({container:"map",zoom:3,center:[-101.43,44.34],style:style as any}); // TODO maplibre types let cache = new ProtocolCache();
maplibregl.addProtocol("pmtiles", cache.protocol);
const map = new maplibregl.Map({
container: "map",
zoom: 0,
center: [0, 0],
style: rasterStyle(props.file) as any,
}); // TODO maplibre types (not any)
map.on("load", map.resize);
return () => { return () => {
map.remove(); map.remove();
}; };
}, []); }, []);
return <MapContainer id="map"></MapContainer>; return <MapContainer id="map" ref={mapRef}></MapContainer>;
} }
export default MaplibreMap; export default MaplibreMap;