Protocol Buffers ããŒã¹ã®å°ç空éãã€ããªãã©ãŒãããã
軜éã»å€æããã»ArrayBuffer ãã€ãã£ã â ãã©ãŠã¶ GIS ã®ã¯ã€ã€ãã©ãŒããã
GeoJSON ã¯å¯èªæ§ãé«ãæ±ããããããå€§èŠæš¡ããŒã¿ã§ã¯ã¹ã±ãŒã«ããªãã ããã¹ã圢åŒã®ãã転ééã倧ãããããŒã¹ã« CPU æéããããã ãã©ãŠã¶ã®ã¡ã€ã³ã¹ã¬ããããããã¯ããã Shapefile ã¯åºãæ®åããŠãããè€æ°ãã¡ã€ã«æ§æã§ Web ã«åããã ã¿ã€ã« MVT 㯠zoom ããšã«åå²ãããããã¯ãŒã¯å¹çãæªãã
GeoPBF 㯠ãã©ãŠã¶ GIS ã®éä¿¡ã¬ã€ã€ãŒ ãšããŠèšèšããããã€ããªãã©ãŒãããã§ããã Protocol Buffers ã®å§çž®å¹çã座æšã®ãã«ã¿ãšã³ã³ãŒãã£ã³ã°ã ArrayBuffer ãã€ãã£ããªèšèšã«ããã転éã»ãã³ãŒãã»Worker éåãæž¡ãã®ãã¹ãŠãé«éã«ãªãã
GeoPBF ãã¡ã€ã«ã¯ Header Section ãš Body SectionïŒFARRAYïŒ ã® 2 éšæ§æã§ããã
| ãã£ãŒã«ã | ã¿ã° | å | 説æ |
|---|---|---|---|
| GTYPE | 8 | Varint | 0:Point 1:MPoint 2:Line 3:MLine 4:Poly 5:MPoly 6:Collection |
| LENGTH | 9 | Packed Varint | ãªã³ã°ã»ãã«ãããŒãã®é ç¹æ°å |
| COORDS | 10 | Packed SVarint | ãã«ã¿å§çž®åº§æšå Xâ,Yâ,ÎXâ,ÎYâ,...ïŒåŸè¿°ïŒ |
| INDEX | 12 | Packed Varint | ã°ããŒãã« KEYS èŸæžãžã®ã€ã³ããã¯ã¹ |
| VALUE | 11 | Repeated Message | NULL/BOOL/INTEGER/FLOAT/STRING/DATE/COLOR/JSON/BLOB/IMAGE |
NAMEã»DESCRIPTIONã»LICENSE ãªã©ã®ã¡ã¿ããŒã¿ã¯ããããŒã«ç¬ç«ããŠæ ŒçŽãããŠããããã ãã€ããªã¹ã©ã€ã¹ã§çŽæ¥æžãæããå¯èœããã£ãŒãã£æ¬äœãåãšã³ã³ãŒãããå¿ èŠããªãã 巚倧ããŒã¿ã»ããã®åç§°å€æŽãã©ã€ã»ã³ã¹æ å ±ã®æŽæ°ãå³åº§ã«å®äºããã
座æšã GeoJSON ã®ããã«æµ®åå°æ°ç¹ããã¹ãã§èšé²ãããšã
çµåºŠ 139.7412345 ã ãã§ 11 ãã€ãæ¶è²»ããã
GeoPBF 㯠2 段éã®å€æã§ãããåæžããã
飿¥ããé ç¹éã®åº§æšå·®ã¯æ°çŸã¡ãŒãã«çšåºŠïŒå°å³ã¹ã±ãŒã«æ¬¡ç¬¬ïŒã§ããã æŽæ°ååŸã®å·®åã¯å°ããªå€ã«ãªãã Varint ã¯å°ããªæŽæ°ãå°ãªããã€ãæ°ã§è¡šçŸããããã é ç¹ãå¯ãªã»ã© 1 é ç¹ãããã®ã³ã¹ããäžãããå°çããŒã¿ã§ã¯æå©ã«åãæ§è³ªã§ããã
GeoJSON ã® "coordinates":[139.741234,35.681234] ã¯çŽ 30 ãã€ãïŒããã¹ãïŒã
GeoPBF ã®ãã«ã¿ Varint 衚çŸã§ã¯åå 8 ãã€ãã以éã®é ç¹ã¯ 2ã3 ãã€ãã
§8.1 ã§å®æž¬ãã 3 ããŒã¿ã»ããã§ã¯ãç¡å§çž® GeoJSON æ¯ã§ 75ã92% ã®åæžã
ããã« gzipïŒCompressionStreamïŒã§ 18ã29% æžãããã®äžç©ã¿ãå°ããã®ã¯ã
gzip ãæ¢ãåé·æ§ãæ§é ã®åŽã§å
ã«é€ããŠããããã§ããã
GeoPBF ã¯åãªãä¿åãã©ãŒãããã§ã¯ãªããäž»èŠ GIS ãã©ãŒãããéã® å€æãã ãšããŠæ©èœããã å ¥åïŒãã³ãŒãïŒãšåºåïŒãšã³ã³ãŒãïŒã®äž¡æ¹ã«å€æãã¹ãæã¡ã GeoPBF ãäžå¿ã«çœ®ãããšã§ä»»æãã©ãŒãããéã®çžäºå€æã 1 ã¹ãããã§å¯èœã«ãªãã
| ãã©ãŒããã | èªã¿èŸŒã¿ | æžãåºã | ç¹èš |
|---|---|---|---|
| GeoJSON | â | â | File/Blob/Object ãã¹ãŠå¯Ÿå¿ |
| Shapefile | â (.zip) | â (.zip) | ãã«ããã¡ã€ã«ã zip åäœã§åŠç |
| KMZ / KML | â | â | Google Earth äºæ |
| GML | â | â | OGC æšæº |
| GPX | â | â | GPS ãã©ãã¯ã»ãŠã§ã€ãã€ã³ã |
| FlatGeoBuf | â | â | é«éã¹ããªãŒãã³ã°å¯Ÿå¿ |
| TopoJSON | â | â | ããããžãŒä¿æ å ¥åºå |
| ç»èšæåä»å°å³ïŒæ³åçïŒ | â (.zip) | â | 14æ¡å°å³ã»çããªãŽã³ããã³ãŒãæã« JGD2011 å¹³é¢çŽè§åº§æšïŒ19 ç³»ïŒâ WGS84 ã宿œãæ¡åŒµåã .zip ã§ Shapefile ãšç«¶åãããã { format: "moj" } ã§æç€ºæå® |
| GeoParquet (v1.5) | â | â | WKB + bbox èŠååã»è¡ã¯ç©ºéé ãäŸåãŒãã®ãªãŒã㯠geopandas / pyarrow / DuckDB ã®åºåãèªã â §13 |
| PMTilesïŒMVTïŒ(v1.5) | â | â | ã¿ã€ã«ã¯ Gint ããåãããããºãŒã å¥ç°¡ç¥åã¯ã©ã³ã¯ã®ãã£ã«ã¿ 1 〠â §13ãé倿ã¯ç¡ãïŒã¿ã€ã«ã¯ç°¡ç¥åã»éååæžã¿ïŒ |
| GeoPBF | â (native) | â (native) | ãã€ãã£ãé«éããŒã |
| Gint | â (decode) | â (encode) | Morton + VW æ¡åŒµïŒåŸè¿°ïŒ |
äž 8 è¡ã¯èªåå€å®ã®å
¥å£ geopbf(input) ãéããGeoParquet ãš PMTiles ã¯èŠæš¡ã倧ããå¿
èŠæã«
èªãã°è¶³ãããããããããå°çšã®ãµããã¹ exportïŒgeopbf/geoparquetã»geopbf/pmtilesïŒ
ã«åããŠããïŒè©³çŽ°ã¯ Â§13ïŒãã©ã¹ã¿ã¯ããã«å¥çµè·¯ã§ãgeopbf/cog 㯠Cloud Optimized GeoTIFF ã
HTTP Range ã§çŽæ¥èªã¿ã倿èªäœãè¡ããªãïŒÂ§12ïŒããããã CLI ã«å¯Ÿå¿ç©ãããã
npx geopbf enc / dec / pmtiles / parquet / parquet2pbf ã GDAL ç¡ãã®çŽ ã® Node ã§åãã
geopbf(input) ã« Fileã»Blobã»ArrayBufferã»
Object ã®ããããæž¡ããŠããæ¡åŒµåã»MIME ã¿ã€ãã»ããžãã¯ãã€ããã
ãã©ãŒããããèªåå€å®ããŠé©åãªãã³ãŒããåŒã³åºãã察å¿ã¯
.geopbf .pbf .geojson .json .topojson .fgb .zip .kml .kmz .gpx .gml .xml
ïŒãããã .gz å¯ã»gzip ã¯çœ²åã§å€å¥ïŒãåŒã³åºãåŽã¯ãã©ãŒããããæèããå¿
èŠããªãã
å¯äžã®äŸå€ãç»èšæåä»å°å³ã§ãæ¡åŒµå .zip ã Shapefile ãšéãªããã
{ format: "moj" } ã§æç€ºããããã³ãŒãã§ããªã File ã¯é»ã£ãŠç©ºã®
FeatureCollection ãè¿ãããæç€ºãšã©ãŒã§èœãšãã
// ã©ããªåœ¢åŒã§ãåã API ã§èªã¿èŸŒãã
const pbf = await geopbf(file); // FileïŒShapefile .zip, GeoJSON, KMZ ...ïŒ
const pbf = await geopbf(arrayBuffer); // ArrayBufferïŒfetch çµæãªã©ïŒ
const pbf = await geopbf(geojsonObject); // GeoJSON ãªããžã§ã¯ãçŽæ¥
const pbf = await geopbf(zip, { format: "moj" }); // ç»èšæåä»å°å³ .zipïŒå¹³é¢çŽè§âWGS84ïŒ
// åãã©ãŒããããžã®ãšã¯ã¹ããŒã
const geojson = pbf.geojson; // â GeoJSON ãªããžã§ã¯ã
const topojson = pbf.topojson; // â TopoJSONïŒããããžãŒä¿æïŒ
const shp = pbf.shape(); // â Shapefile (.zip Blob)
const kmz = pbf.kmz(); // â KMZ Blob
const gml = pbf.gml(); // â GML æåå
const fgb = await pbf.fgb(); // â FlatGeoBuf ArrayBuffer
// v1.5 â 倧ãã 2 圢åŒã¯å°çšã®ãµããã¹ export ããïŒÂ§13ïŒ
import { toPMTiles } from "geopbf/pmtiles";
import { toGeoParquet, fromGeoParquet } from "geopbf/geoparquet";
const { buffer } = await toPMTiles(pbf, { maxZoom: 12 }); // â PMTilesïŒMVTïŒãã€ãå
const pq = await toGeoParquet(pbf); // â GeoParquet ãã€ãå
const back = await fromGeoParquet(u8); // â GeoParquetïŒgeopandas/DuckDB åºåãå¯ïŒ
// ãã€ãã£ãä¿åïŒgzip æžã¿ .geopbfïŒ
await pbf.save('coastline'); // â coastline.geopbf ãããŠã³ããŒã
ã¢ã³ãã¡ãªãžã¢ã³ïŒçµåºŠ ±180°ïŒã¯å°çããŒã¿ã«ããã代衚çãªèœãšã穎ã§ããã ããŒãªã³ã°æµ·ããŸãããããªããªãŽã³ã¯ãå€ãã®ãã©ãŒãããã§åº§æšã +179° ãã â179° ãžãžã£ã³ããã åäžãªã³ã°ãšããŠæ ŒçŽãããã ã¬ã³ãã©ãŒã»ç©ºéã€ã³ããã¯ã¹ã»ã¯ãªããã³ã°åŠçã®ãããããã®ãããªããŒã¿ã§ã¯èª€åäœããã
GeoPBF ã¯ã¢ã³ãã¡ãªãžã¢ã³ã®æ£ç¢ºãããã©ãŒãããäžå€æ¡ä»¶ãšããŠæ±ãïŒ GeoPBF ã«æ ŒçŽããããžãªã¡ããªã¯ãã¹ãŠã¢ã³ãã¡ãªãžã¢ã³ã§åæãããŠããããšãä¿èšŒãããã ã¢ã³ãã¡ãªãžã¢ã³ããŸããããªãŽã³ã¯ãã©ãŒãããå ã«ååšã§ããªãã
setFeature() ã¯ãã¹ãŠã®ãã£ãŒãã£ããããã¡ã«æžã蟌ãåã«
antimeridianFeature(q) ãåŒã³åºã â ãœãŒã¹ãã©ãŒãããã«é¢ãããã
GeoJSONã»Shapefileã»KMZã»GMLã»GPXã»FlatGeoBufã»TopoJSON â ãã¹ãŠåããã¹ãéãã
åŒã³åºãåŽã¯ãžãªã¡ããªãäºååŠçããå¿
èŠããªãã
ã«ããã¯åçŽãª ±180° 座æšã¯ã©ã³ãã§ã¯ãªãã
antimeridianCut() ã¯çé¢ã®å
¬åŒã䜿ã£ãŠã
å亀差ã»ã°ã¡ã³ããã¢ã³ãã¡ãªãžã¢ã³ãšäº€ããæ£ç¢ºãªç·¯åºŠãèšç®ãã â
çµç·åæãé¡èãªæ¥µè¿åã§ã¯ç¹ã«éèŠã§ããã
ã«ããåŸã®åãµãããªãŽã³ã¯ãæ£ç¢ºãªäº€ç¹ç·¯åºŠã«é ç¹ãæ¿å
¥ããæå¹ãªéãªã³ã°ãšãªãã
// 亀差æ€åº: 笊å·å転 + ã¹ãã³ 180° è¶
â ã¢ã³ãã¡ãªãžã¢ã³è¶ãïŒåãªã座æšãžã£ã³ããšã®åºå¥ïŒ
// p[i][0] * p[i+1][0] < 0 && abs(p[i][0] - p[i+1][0]) > 180
// çé¢äº€ç¹ç·¯åºŠïŒå€§åã®å
¬åŒïŒ
// x = sin(Îlat/2)·sin(avgLng)·cos(halfÎlng) - sin(avgLat)·cos(avgLng)·sin(halfÎlng)
// z = cos(lat0)·cos(lat1)·sin(Îlng)
// intersectLat = atan2(x, |z|) â çé¢äžã§å¹ŸäœåŠçã«æ£ç¢º
Morton ã³ãŒãã¯åº§æšãæçãªé åå ã«åãŸãããšãåæãšããã ±180° ããŸãããžãªã¡ããªã¯æŽæ°ç©ºéã®ç«¯ãã端ãžãžã£ã³ããããã㪠Morton ã³ãŒããçæãã äºåæ¢çŽ¢ãšç©ºéè¿æ¥æ§ã®ä¿èšŒãç Žå£ããã ãšã³ã³ãŒãæã«ã«ãããæžãŸããããšã§ãGeoPBF ã¿ã€ã«å ã®ãã¹ãŠã® Morton ã³ãŒãã äžæµã§ç¹å¥æ±ãäžèŠãªç©ºéçäžè²«æ§ãæã€ã
GeoPBF ã®å
éšè¡šçŸã¯äžè²«ã㊠ArrayBuffer ã§ããã
ããã¯ãã©ãŠã¶ã®äžŠååŠçåºç€ïŒWeb WorkersïŒãšæ·±ãçµ±åããããã®èšèšäžã®éžæã§ããã
3 ã€ã®éèŠãªç¹æ§ãããããã
// ArrayBuffer ã¯ã¡ã€ã³ã¹ã¬ãã â Worker ãžãŒãã³ããŒã§è»¢é
// ïŒã³ããŒã§ã¯ãªãæææš©ã®ç§»å â ã¡ã¢ãªæ¶è²»ãå¢ããªãïŒ
worker.postMessage(pbf.arrayBuffer, [pbf.arrayBuffer]);
// 転éåŸ pbf.arrayBuffer 㯠detachedïŒã¡ã€ã³ã¹ã¬ããããã¢ã¯ã»ã¹äžèœïŒ
// Worker åŽ
onmessage = ({ data: buf }) => {
const view = new DataView(buf); // å³åº§ã«ãã³ãŒãéå§
postMessage(result, [result]); // åŠçåŸããŒãã³ããŒã§è¿åŽ
};
// gint ãã³ãŒãåŸã®åº§æšããŒã¿ã SharedArrayBuffer ã«å±é
// â è€æ°ã®æç» Worker ãåæã«ãã³ããŒãªãã§åç
§ã§ãã
const sab = new SharedArrayBuffer(buf.byteLength);
new Uint8Array(sab).set(new Uint8Array(buf));
// tile worker à N ã䞊åã§ LOD ãã£ã«ã¿ & æç»
tileWorkers.forEach(w => w.postMessage({ sab, zoom, viewport }));
// ä¿åæ: ãã©ãŠã¶ãã€ãã£ãã® gzip å§çž®ïŒå€éšã©ã€ãã©ãªäžèŠïŒ
let blob = new Blob([buf]);
blob = await (new Response(
blob.stream().pipeThrough(new CompressionStream("gzip"))
)).blob();
postMessage(new File([blob], `${name}.geopbf`, { type: "application/x-geopbf" }));
GeoJSON ã¯ãªããžã§ã¯ãã°ã©ãïŒJSONïŒã§ãããWorker ã«æž¡ãã«ã¯
JSON.stringify â 転é â JSON.parse ãšãã
ã·ãªã¢ã©ã€ãº/ãã·ãªã¢ã©ã€ãºãå¿
èŠã§ãå€§èŠæš¡ããŒã¿ã§ã¯æ°çŸããªç§ãæ¶è²»ããã
GeoPBF ã¯æåãã ArrayBuffer ã§ããããã
ãŒãã³ããŒè»¢é ã§ Worker éãç§»åã§ãã
åãåãåŽã¯ãã€ã³ã¿ãé²ããã ãã§åº§æšãèªããã
ã¡ã€ã³ã¹ã¬ããã¯äžåãããã¯ãããªãã
GeoPBF ã« gint ãšã³ã³ãŒããé©çšãããšãåé ç¹ã®åº§æšã 64bit æŽæ° 1 〠ã«ããã¯ãããã ãã® 64bit æŽæ°ã¯ Morton ã³ãŒããš VW ãŠã§ã€ããåæã«æ ŒçŽããã
1 ã€ã® 64bit æŽæ°ã«ãã©ãïŒMortonïŒããšãã©ã® LODïŒVW ã©ã³ã¯ïŒããåæã«åããããšã§ã 空éã¯ãšãªãš LOD ãã£ã«ã¿ãåçŽãªæŽæ°æŒç®ã ãã§å®çµããã
// L1 / L2 å€å®ïŒæäžäœãããïŒ
const isAnchor = (code >> 63n) === 1n;
// Morton ã³ãŒãåãåºãïŒL2 ã®å Žåäžäœ 58bitïŒ
const mortonCode = code & 0x7FFFFFFFFFFFFFF8n; // bits 3-62
// VW ãŠã§ã€ãã©ã³ã¯åãåºãïŒL2 ã®å Žåäžäœ 6bitïŒ
const vwRank = Number(code & 0x3Fn); // bits 0-5 â 0ã63
// ãºãŒã ã¬ãã« z ã§ã® LOD ãã£ã«ã¿ïŒ1px² ã«å¯Ÿå¿ããã©ã³ã¯éŸå€ïŒ
const minRank = rankFromZoom(z); // z ã倧ããã»ã©äœãéŸå€ïŒããå€ãã®é ç¹ïŒ
const visible = isAnchor || vwRank >= minRank;
æ§æ ŒãšèŠæš¡ã®ç°ãªãå ¬éããŒã¿ 3 çš®ããåäžãã·ã³ã§ç«¯ãã端ãŸã§å€æããŠå®æž¬ããã ã©ã®è¡ãåãå°ç©ãä¿æããŠãããåäœã¯ 10 é² MBã
| ãã©ãŒããã | NE 10m admin_0 äžçã®åœç v5.1.1 |
åœåæ°å€æ
å ± N03ïŒ2026ïŒ å šåœåžåºçºæç |
TIGER 2024 ZCTA5 ç±³åœ ZIP ã³ãŒãåºå |
|---|---|---|---|
| å°ç©æ° | 258 | 125,130 | 33,791 |
| é ç¹æ° | 548,469 | 15,669,416 | 51,258,797 |
| åº§æšæ Œå | 10â»â¶Â°ïŒ0.11 mïŒ | 10â»â¶Â°ïŒ0.11 mïŒ | 10â»âµÂ°ïŒ1.1 mïŒ |
| ShapefileïŒ.shp+.dbf+.shxïŒ | 9.7 | 282.8 | 825.3 |
| â +.prj ã zip â9 | 4.9 | 174.8 | 530.5 |
| GMLïŒ.xmlïŒ | â | 580.7 | â |
| â + gzip â9 | â | 123.2 | â |
| GeoJSON | 13.3 | 579.6 | 1,089.6 |
| â + gzip â9 | 4.7 | 138.6 | 265.8 |
| FlatGeobuf | 9.3 | 266.9 | 826.8 |
| â + gzip â9 | 5.4 | 156.3 | 421.9 |
| GeoParquetïŒWKBã»zstdã»bbox åïŒ | 4.5 | 124.5 | 333.7 |
| GeoPBF | 3.3 | 48.6 | 117.8 |
| GeoPBF + gzip â9 ïŒé åžåœ¢ïŒ | 2.7 | 34.4 | 95.2 |
é ç¹ãããã®ãã€ãæ°ïŒç¡å§çž® â å§çž®åŸïŒããã©ãŒãããã®å·®ã¯ããã«åºãã
| é ç¹ãããïŒãã€ãïŒ | NE 10m admin_0 | N03 | ZCTA5 |
|---|---|---|---|
| Shapefile | 17.7 â 9.0 | 18.1 â 11.2 | 16.1 â 10.3 |
| GeoJSON | 24.2 â 8.6 | 37.0 â 8.8 | 21.3 â 5.2 |
| FlatGeobuf | 17.0 â 9.8 | 17.0 â 10.0 | 16.1 â 8.2 |
| GeoPBF | 6.0 â 4.9 | 3.1 â 2.2 | 2.3 â 1.9 |
ã©ã¡ããé ç¹ããã 16ã18 ãã€ããçç±ãåãã§ã1 ç¹ã«ã€ã IEEE-754 ã® double ã 2 æ¬ã ãžãªã¡ããªã®åœ¢ã«ããã眮ãããã§ãããäž¡è ã®å·®ã¯ã¬ã³ãŒãããããšå±æ§ã®æã¡æ¹ã§ãã£ãŠåº§æšã§ã¯ãªãã 1998 幎㮠Shapefile ãš 2019 幎㮠FlatGeobuf ã®ããã ã« 20 幎ãã£ãŠããžãªã¡ããªéšã¯åããªãã£ãã GeoPBF ã¯ä»£ããã«æŽæ°æ Œåäžã®ãã«ã¿ã眮ãã®ã§ãã³ã¹ãã¯é ç¹å¯åºŠãšãšãã« äžããã æ°ã¡ãŒãã«å»ã¿ã®å¢çç·ãªãé ç¹ããã 2ã3 ãã€ãã屿§åã®å€ãçãªãžãªã¡ããªãªãããå°ãå€ãã
gzip åå£«ã§æ¯ã¹ããšãGeoPBF 㯠GeoJSON ã® 1.8ã4.0 åã® 1 ã§ãããç¡å§çž®ã®è¡ã瀺ã 5ã17 åã§ã¯ãªãã ã©ã®ãã©ãŒããããå®éã¯å§çž®ããŠé ãã®ã ããããã¡ããæ£å³ã®æ°åã«ãªãã å§çž®ãäžçªå¹ãã®ã¯ããã¹ãïŒGeoJSON ã¯å ã® 24ã36%ãGML 㯠21% ãŸã§èœã¡ãïŒã double ç³»ã®ãã€ããªã¯äžåºžïŒshapefile 51ã64%ã»FlatGeobuf 51ã59%ïŒé£ãåã座æšã¯ double ã®äžäœãã€ãã å ±æããã®ã§å¹ããå¹ããªãã®ã¯äžäœã®ä»®æ°ãããã ãïŒã ãã㊠GeoPBF ãäžçªå¹ããªãïŒ71ã82%ïŒãå§çž®åšãæ¢ãåé·æ§ããæ§é ã®åŽã§å ã«æœ°ããŠããããã§ããã ç¡å§çž®ã®è¡ã¯ãã³ãŒããã¡ã¢ãªäžã§æ©ãéãå§çž®åŸã®è¡ãåç·ãæž¡ãéã«ãããã
åçŸã¯ npx geopbf enc ãš npx geopbf parquetãFlatGeobuf ã®è¡ã¯å
¬åŒã®
flatgeobuf npm ã©ã€ã¿ã§åãå°ç©ãæžãããã®ãzip è¡ã¯ .shp+.dbf+.shx+.prj ã deflate â9 ã§
ãã¡ãã§åºãçŽããŠããã3 ããŒã¿ã»ãããåãæé ã§æž¬ãããã§ãã â é
åž zip ã¯äžèº«ããŸã¡ãŸã¡ã§
ïŒNatural Earth 4.9 MB 㯠HTML README å
¥ããTIGER 528.8 MB 㯠ISO ã¡ã¿ããŒã¿ XML å
¥ãã
åœåæ°å€æ
å ± N03 803.2 MB 㯠GML ãš shapefile ãš GeoJSON ãå
šéšå
¥ãïŒãã®ãŸãŸã§ã¯æ¯èŒã«ãªããªãã
shapefile ã¯åå
žã® double ããã®ãŸãŸæã€ãã粟床ã«ããã 1 ç¹ 16 ãã€ãåºå®ãªã®ã§ãµã€ãºè¡ã¯åœ±é¿ãåããªãã
| ææš | GeoJSON | Shapefile | MVTïŒã¿ã€ã«ïŒ | GeoPBF |
|---|---|---|---|---|
| ãšã³ã³ãŒãã£ã³ã° | ããã¹ã UTF-8 | ãã€ããªïŒè€æ°ãã¡ã€ã«ïŒ | PBF ãã€ã㪠| PBF + ãã«ã¿ + Varint |
| ãã¡ã€ã«æ° | 1 | .shp + .dbf + .shx + ... | zoom à tile æ° | 1 |
| Web 転éå¹ç | äœïŒããã¹ãå€§ïŒ | äž | äžïŒzoom åå²ïŒ | é«ïŒãã€ã㪠+ gzipïŒ |
| Worker 転é | JSON ã·ãªã¢ã©ã€ãºå¿ èŠ | å€æå¿ èŠ | ArrayBuffer | ArrayBuffer ãŒãã³ã㌠|
| LOD ãµããŒã | ãªã | ãªã | zoom å¥ãã¡ã€ã« | VW ãŠã§ã€ãå å |
| 空éã€ã³ããã¯ã¹ | ãªã | .shxïŒç·åœ¢ïŒ | ã¿ã€ã«åº§æš | ãã¡ã€ã«ã«ã¯ç¡ãïŒMorton 64bit ã¯èªã¿èŸŒã¿æã«ã¡ã¢ãªäžã§äœãïŒ |
| ãã©ãŒããã倿 | èŠå€éšã©ã€ãã©ãª | èŠå€éšã©ã€ãã©ãª | éå®ç | 12 ãã©ãŒãããå èµ |
| ããããžãŒ | ãªã | ãªã | ãªã | ã¢ãŒã¯å ±æã»å¢çæŽå |
// ââ èªã¿èŸŒã¿ ââââââââââââââââââââââââââââââââââââââââââââââ
const pbf = await geopbf(input, options);
// input: File | Blob | ArrayBuffer | GeoJSON object | URL string
// options: { name, gint, nocache, clean }
// gint: false â ããããžãŒãšã³ã³ãŒãïŒgint倿ïŒãã¹ããã
// nocache: true â IndexedDB ãã£ãã·ã¥ãç¡èŠããŠåååŸ
// clean: true â ããããžãŒæŽåã¯ãªãŒã³åŠçãå®è¡
// ââ ããŒã¿ã¢ã¯ã»ã¹ ââââââââââââââââââââââââââââââââââââââââââ
pbf.length // ãã£ãŒãã£æ°
pbf.keys // 屿§åé
å ['name', 'rank', ...]
pbf.arrayBuffer // ç ArrayBufferïŒWorker 転éçšïŒ
pbf.geojson // GeoJSON FeatureCollection ã«å€æ
pbf.topojson // TopoJSON ã«å€æïŒããããžãŒä¿æïŒ
pbf.getFeature(i) // i çªç®ã®ãã£ãŒãã£ã ãããã³ãŒã
// ââ ãšã¯ã¹ããŒã ââââââââââââââââââââââââââââââââââââââââââââ
await pbf.save('name') // .geopbfïŒgzipïŒãšããŠããŠã³ããŒã
pbf.shape() // Shapefile .zip
pbf.kmz() // KMZ
pbf.gml() // GML æåå
pbf.gpx() // GPX æåå
await pbf.fgb() // FlatGeoBuf ArrayBuffer
// ââ ã¡ã¿ããŒã¿ O(1) æŽæ° ââââââââââââââââââââââââââââââââââââ
pbf.header({ name: 'New Name', description: '...', license: 'MIT' });
// ââ æç» âââââââââââââââââââââââââââââââââââââââââââââââââââ
pbf.draw(canvasCtx, { fill: '#5a8050', stroke: '#a8d47e' });
v1.1.0 以éãnpm ããã±ãŒãžã¯äž»èŠ Web å°å³ã©ã€ãã©ãªåãã®ã¢ããã¿ã忢±ããã
ãããããã¹ãåŽã©ã€ãã©ãªã import ããªãäŸåãŒãã®ãµããã¹ export ã§ããã
å
±éããŒãã®äžã«èŒãããã¡ã€ã«å
šäœ gzip ã¯ããžãã¯ãã€ãã§å€å¥ããŠééçã«å±éãããã³ãŒãã¯
new Function ã䜿ããªã CSP ã»ãŒãã® noeval ã¢ãŒããproperties ã¯
JSON-safe ãžååããïŒDate â ISO æååãbbox â çŽ ã®é
åããã€ããªå€ã¯é€å»ïŒã
åã©ã€ãã©ãªã® worker åãæž¡ããå£ããªãããã®åŠçœ®ã§ããã
| import | 圢 | 察象 |
|---|---|---|
geopbf/maplibre |
addProtocol ãã³ãã© | MapLibre GL JS (v4+) |
geopbf/leaflet |
L.GeoJSON ãµãã¯ã©ã¹ | Leaflet (1.x) |
geopbf/openlayers |
VectorSource loader ãã¡ã¯ã㪠| OpenLayers (6+) |
geopbf/loaders |
loaders.gl Loader ãªããžã§ã¯ã | deck.gl · kepler.gl |
geopbf/load |
çŽ ã® loadGeopbf(url) |
Cesium · D3 ãªã© GeoJSON ãé£ã¹ãå šãŠ |
// ââ MapLibre â ãããã³ã«ãœãŒã¹ ââââââââââââââââââââââââââââ
import { geopbfProtocol } from "geopbf/maplibre";
maplibregl.addProtocol("geopbf", geopbfProtocol);
map.addSource("rail", { type: "geojson", data: "geopbf://https://âŠ/N02-25_RailroadSection" });
// ââ Leaflet â ã¬ã€ã€ãŒãã©ã°ã€ã³ âââââââââââââââââââââââââââ
import { extendLeaflet } from "geopbf/leaflet";
extendLeaflet(L);
L.geoPBF("geopbf://https://âŠ").on("load", e => console.log(e.meta)).addTo(map);
// ââ OpenLayers â ãœãŒã¹ loader ââââââââââââââââââââââââââââ
import { makeGeopbfLoader } from "geopbf/openlayers";
const source = new VectorSource({ loader: makeGeopbfLoader(url, new GeoJSON()) });
// ââ deck.gl â loaders.gl ââââââââââââââââââââââââââââââââââ
import { GeoPBFLoader } from "geopbf/loaders";
new GeoJsonLayer({ data: url, loaders: [GeoPBFLoader] });
// ââ ããä»¥å€ â æ±çšããŒã âââââââââââââââââââââââââââââââââ
import { loadGeopbf } from "geopbf/load";
const r = await loadGeopbf(url); // { geojson, name, license, attribution, minZoom, maxZoom, ⊠}
viewer.dataSources.add(await Cesium.GeoJsonDataSource.load(r.geojson));
åºå
žè¡šèšã¯ãã¡ã€ã«å
éšã«æ ŒçŽãããïŒÂ§2ïŒãLeaflet ãš OpenLayers ã®ã¢ããã¿ã¯ãããã®
attribution ãå°å³ã®ã¯ã¬ãžãã衚瀺ãžèªåé
ç·ããïŒæç€ºæå®ãåªå
ïŒã
loadGeopbf ã¯ãããã¡ã¿ããŒã¿äžåŒãè¿ããããæåé
ç·ãå¯èœã§ããã
åŒã³åºãåŽã®å¶çŽã¯ 1 ç¹ã®ã¿ã§ãMapLibre ã§ã¯å
åŽ URL ã絶察圢ã§èšè¿°ãã
ïŒgeopbf://https://âŠãpmtiles ãšåãæµåïŒãMapLibre ã¯ãœãŒã¹ URL ã
new URL() ã§æ£èŠåãããããçžå¯Ÿåœ¢ã¯åŸ©å
ã§ããªãã
GeoPBF ã¯éãããã©ãŒãããã§ããããã¡ã€ã«ã¯èª°ã§ãçæã§ããURL 1 ã€ã§é
åžã§ããããããã£ãŠè¡šç€ºçš
ãããã㣠â @tipã»@popãããã³ãã¥ãŒã¢ã HTML ãšããŠæç»ãããããã
ãããã㣠â ã¯éä¿¡é Œå
¥åãšããŠæ±ãå¿
èŠããããæ±ãã誀ãã°ã现工ããããã¡ã€ã«ã
ããŒã«ããããå¹ãåºããéããæç¹ã§ããŒãžå
script ãå®è¡ã§ããïŒXSSïŒã察åŠã¯å®ç³ã©ãããæååã
innerHTML ã«å
¥ãçŽåãããªãã¡åºååŽã®å¢çã§æ¶æ¯ãããä¿ååŽã§ã®æ¶æ¯æžã¿ã
åæã«ã¯ã§ããªãããã¡ã€ã«ã¯ãšãã£ã¿ãçµç±ãããã¥ãŒã¢ãžå°éãããããã§ããã
// åºååŽã®å¢çïŒinnerHTML ã®çŽåã§æ¶æ¯ãã
import { sanitizeHTML } from "geopbf/sanitize";
el.innerHTML = sanitizeHTML(feature.properties["@pop"]);
sanitizeHTML ã¯äŸåãŒãã®èš±å¯ãªã¹ãåŒãã£ã«ã¿ã§ãããæŽåœ¢ã¿ã°ã»ãªã¹ãã»è¡šã»ç»åã»ãªã³ã¯ã¯
ééããscriptã»iframeã»svg çã¯äžèº«ããšç Žæ£ãæªç¥ã¿ã°ã¯æ®»ã ãã
å¥ãã§äžèº«ãæ®ããã€ãã³ã屿§ãš style ã¯ééããªããhref ã¯
http(s)/mailtoãimg src ã¯
http(s)/data:image ã®ã¿ãèš±å¯ããããªã³ã¯ã«ã¯
target="_blank" rel="noopener noreferrer" ã匷å¶ãããDOM ã«è§Šããã®ã¯åŒã³åºãæã®ã¿ã§ã
worker ããã® import ãå®å
šã§ããã§10 ãšã®åœ¹å²åæ
ã¯æç¢ºã§ã
sanitizeProperties ã¯å€ã JSON å®å
šã«ïŒåã®åé¡ïŒãsanitizeHTML ã¯
衚瀺å®å
šã«ïŒããŒã¯ã¢ããã®åé¡ïŒãããäž¡è
ã¯äœµçšããã
COGïŒCloud Optimized GeoTIFFïŒã¯ HTTP Range ã§éšåèªã¿ã§ããéçãã¡ã€ã«ã§ãããGeoPBF ãšåã
ããµãŒããŒäžèŠãã®æ§æãã©ã¹ã¿ãžé©çšãã圢åŒã§ãããv1.4 ã® geopbf/cog ã¯äŸå远å ãŒãã®
èªåãªãŒãã§ãããã㯠16KB ã® range 1 æ¬ã§èªã¿ãã¿ã€ã«èŠæ±ã¯ãªãã»ããé ã«
æŽåããŠé£æ¥ range ã1 ãªã¯ãšã¹ããžåäœïŒcoalesceïŒãããè§£åãšåæåœ±ã¯ worker
ããŒã«ãJPEG/WebP ã¯ãã©ãŠã¶ã®ãã€ãã£ããã³ãŒããžæž¡ãã宿ž¬å€ã¯
cog.metrics() ãéæè¿ãïŒrange æ¬æ°ã»coalesce å§çž®çã»ãã³ãŒãæéïŒã
import { openCog } from "geopbf/cog";
const cog = await openCog("https://âŠ/TCI.tif"); // range 1æ¬ã§ãããå®èª
const bm = await cog.renderXYZ(14, x, y); // Web ã¡ã«ã«ãã«ãž warp æžã¿ ImageBitmap
察å¿ãµãã»ããã¯å
Œ
± COG é
ä¿¡ã®å®å¢ã«åãããŠãããã¿ã€ã«/ã¹ããªããäž¡ã¬ã€ã¢ãŠãã»BigTIFFã»
å§çž® none/deflate/LZW/JPEG/WebPã»predictor 2ã»uint8 RGB(A)/ãã¬ãã/åãã³ã uint16/int16/float32
ïŒèªåããŒã»ã³ã¿ã€ã«ã»ã¹ãã¬ãããGDAL_NODATAâéæïŒã»CRS 㯠EPSG:4326/3857/UTM
ïŒKrÃŒger n çŽæ° 6 次ïŒnm 粟床ã®èªåå®è£
ïŒããããå€ãããã¡ã€ã«ã¯é»ã£ãŠå£ããã®ã§ã¯ãªãæç€ºãšã©ãŒã§
æåŠããgdal_translate -of COG ã«ããäºåå€æãæ¡å
ãããMapLibre ãžã¯
geopbf/maplibre-cog ã® cog:// ãããã³ã«ãLeaflet ãžã¯
geopbf/leaflet-cog ã® GridLayer ãããããã 1 è¡ã§æ¥ç¶ããã
CLI 㯠npx geopbf cog info <url> --bench ãæ§é ãšå®æž¬å€ãåºåããã
v1.4 ãŸã§ã®å€æããã¯å
¥ååŽã®ã¿ãæ±ã£ãŠãããv1.5 ã§åºååŽãéããGeoPBF 㯠Mapbox Vector Tiles ã®
PMTiles ã¢ãŒã«ã€ãããŸã㯠GeoParquet ãã¡ã€ã«ãšããŠæžãåºããã
GeoParquet ããã®é倿ãå¯èœã§ãããäŸå远å ã¯ãªããããã pako ãåé€ããã
å§çž®ã»äŒžé·ã¯ãã¹ãŠãã©ãããã©ãŒã åŽã® codec ãçŽæ¥åŒã¶ïŒãã©ãŠã¶ãš worker ã¯
CompressionStreamãNode 㯠node:zlibïŒãæ®ãå®è¡æäŸå㯠pbf 1 æ¬ã ã£ããã
1.6 ã§ protobuf ã®ã¯ã€ã€èªã¿æžããèªåã«æã¡èŸŒã¿ããããåã£ããäŸåã¯ç©ºã§ããã
# CLI â çŽ ã® Node ã§åãïŒãã€ãã£ããã«ãäžèŠïŒ
npx geopbf pmtiles countries.geopbf countries.pmtiles --maxzoom 10
npx geopbf parquet countries.geopbf countries.parquet
npx geopbf parquet2pbf countries.parquet back.geopbf # é倿
import { toPMTiles } from "geopbf/pmtiles";
import { toGeoParquet } from "geopbf/geoparquet";
const pbf = await geopbf(file, { gint: true });
const { buffer, stats } = await toPMTiles(pbf, { maxZoom: 12 }); // stats.engine â "gpu" | "cpu"
const pq = await toGeoParquet(pbf); // pq.buffer, pq.geo
ã¿ã€ã«ã¯çã®ãã£ãŒãã£ã§ã¯ãªã Gint ããåãã ortho-earth ã GPU ã§æããŠãã
掟çãããã¡ãã®ãã®ã§ãããå
±æå¢ç㯠1 æ¬ã® arc ãšããŠäžåºŠã ãååšããåé ç¹ã¯
VisvalingamâWhyatt ã®ã©ã³ã¯ãæã€ããããã£ãŠãºãŒã å¥ã®ç°¡ç¥åã¯
rank >= éŸå€ ã®ãã£ã«ã¿ 1 ã€ã«åž°çããé£ãåã 2 ã€ã®ããªãŽã³ã¯å¿
ç¶çã«åäžã®
é ç¹åãžç°¡ç¥åããããã©ã®ãºãŒã ã§ãééãã¹ãªããŒãçããªããéŸå€ã¯ arc ããšã«
DouglasâPeucker åè§£ãšçªãåãããŠèŒæ£ããŠãããããé ç¹æ°ã¯ tippecanoe ã®çŽ 10% 以å
ã«åãŸãã
ãã€ã©ã®é ç¹ãæ®ããã¯ã©ã³ã¯ã決ããã®ã§ãå¢çã®äž¡åŽã¯åäžã®çµæã«ãªãã
Natural Earth 10m countriesïŒ258 ããªãŽã³ã»48 äžé ç¹ã»z0â10ã»4 ã³ã¢ NodeïŒã§ã¯ã
GeoJSON ããçŽ 6.5 ç§ã§ 573,896 ã¿ã€ã« / 151.0MBãtippecanoe v2.82 㯠58 ç§ã§
573,885 ã¿ã€ã« / 147MB ã§ãããã¿ã€ã«éåã¯æ°æã®å·®ã§äžèŽãããå
éšã¿ã€ã«ã¯ã¬ã€ã€åãš geopbf ãæžã
ãã£ãŒã㣠id ã®åãé€ãã°ãã€ãåäœã§åã倧ããã«ãªããåãåŠçã« GDAL ã® PMTiles
ãã©ã€ãïŒ3.12ïŒã¯ 780 ç§ãèŠãããUS Census ZCTA5ïŒ33,092 éµäŸ¿çªå·ãšãªã¢ã»5,200 äžé ç¹ã»z0â12ïŒ
ã§ã¯ 47 ç§ã§ 238,322 ã¿ã€ã« / 251MBãtippecanoe 㯠251 ç§ã§ 238,319 ã¿ã€ã« / 246MBã
MapLibre ã® z3/z7/z11 ã§æãæ¯ã¹ãŠãå·®ã¯å€å¥ã§ããªãã
Parquet ã®è¡ã¯ç©ºéé ã«äžŠã¹ãŠåºåããã ãã£ãŒã㣠bbox ã®äžå¿ã Sort-Tile-Recursive ã§
è©°ããïŒorder: "str" ãæ¢å®ïŒãå row group ã® bbox çµ±èšãäºãã«éãªããªã
åºç»ãèŠããããç¯å²æ¡ä»¶ãæŒãäžããŠèªãåŽïŒDuckDBã»pyarrow datasetsã»GeoPandasïŒã¯å®éã«äº€å·®ãã
row group ã ããååŸããã100 äžç¹ã 50 row group ã«ãã宿ž¬ã§ã¯ãrow group ã®éãªãæ¯ã
ç¡ãœãŒã 24.5 â Morton 0.87 â Hilbert 0.28 â STR 0.00ãåžåãµã€ãºã®åãåãããè§Šã row group ã¯
50 ãã 1â2 ãžæžããäºå£åè£æ°ã® Spatial sort for well-packed GeoParquetïŒCNG Japan 2026ïŒ
ã«å£ã£ãå®è£
ã§ããã
GPU ãæ ãç¯å²ã æåœ±ã»ãºãŒã å¥ã©ã³ã¯ãã£ã«ã¿ã»æŽæ°âdouble 倿ã»bbox ãªãã¯ã·ã§ã³ã¯ãWebGPU ãããã° GPU ã§èµ°ããç¡ããã°åãæŽæ°æŒç®ã CPU ã§èµ°ãããã ã©ã¡ãã®çµè·¯ã§ãåºåã¯ãã€ãäžèŽã§ãããheadless Chromium ã§ã«ãŒãã«åäœã«æ€èšŒããŠããã ãã ããããã®æ®µãå ããã®ã¯ã宿ž¬ããã©ã®ããŒã¿ã§ãä»äºå šäœã® 3â6% ã«ãããªããæ¯é çãªã®ã¯ ãªã³ã°çµã¿ç«ãŠã»ã¯ãªããã³ã°ã»MVT ãšã³ã³ãŒãã»gzip ã®åŽã§ãããGPU ã¯ãã©ãŠã¶ã®ã¡ã€ã³ã¹ã¬ããã 空ããŠããããã®ææ®µã§ãã£ãŠããã®å€æåšãéãçç±ã§ã¯ãªããé床㯠Gint ãšçµã¿ç«ãŠæ®µã«ç±æ¥ããã
é倿ã fromGeoParquet() 㯠GeoParquet ã GeoPBF ãžæ»ãã
äŸåãŒãã® Parquet ãªãŒãïŒThrift compact ããã¿ã»DataPage v1/v2ã»PLAIN ãšèŸæžãšã³ã³ãŒãã£ã³ã°ã»
codec 㯠none / snappy / gzip / zstdïŒãš WKB ããŒãµã®çµã¿åããã§ãããgeopbf ãæžãããã¡ã€ã«ã¯
ãããåäœã§åŸ©å
ãããGeoPandasã»pyarrowã»DuckDB ãæžãããã¡ã€ã«ãåããã£ãŒãã£ãšããŠèªãããCRS ã¯
çµç·¯åºŠïŒCRS84 / EPSG:4326ïŒã«éãããã以å€ã¯ ignoreCrs ãæž¡ããªãéãæåŠããã
PMTiles ã«åçã®é倿ã¯çšæããŠããªããã¿ã€ã«ã¯ç°¡ç¥åããéååãããŠãããããå¯èœãªã®ã¯è¿äŒŒçãª
åæ§æã ãã§ããããããé倿ãšç§°ããããšã¯ããªãã