├── .eslintrc.cjs ├── .gitattributes ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── dependabot.yml ├── pull_request_template.md └── workflows │ ├── containers.yml │ ├── pull-request.yml │ ├── push.yml │ ├── screenshot.yml │ └── smoke-test.yml ├── .gitignore ├── .hyperfine.json ├── .prettierrc.cjs ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── docs ├── README.md ├── configuration.md ├── deployment.md ├── developer-guide │ ├── API │ │ └── .gitignore │ ├── README.md │ ├── cli-methods │ │ ├── bundle-the-basemaps-assets-archive.md │ │ └── bundle-the-basemaps-config-file.md │ ├── run-basemaps-locally.md │ └── server-methods │ │ ├── serve-basemaps-with-bundled-config-file.md │ │ └── serve-basemaps-with-collection-of-geotiff-files.md ├── examples │ ├── _overview.md │ ├── index.leaflet.xyz.3857.html │ ├── index.maplibre.opacity.3857.html │ ├── index.maplibre.vector.3857.html │ ├── leaflet.xyz.3857.md │ ├── maplibre.opacity.3857.md │ └── maplibre.vector.3857.md ├── index.css ├── operator-guide │ ├── README.md │ ├── cog-quality.md │ ├── empty-tiles.md │ ├── gebco.md │ ├── quick-start.md │ ├── relief-shade.md │ ├── static │ │ ├── 2023-06-26-gisborne-2023.png │ │ ├── projection_beehive_fakenztm2000.webp │ │ ├── projection_beehive_nztm2000quad.webp │ │ ├── projection_nztm2000quad_0_0_0.webp │ │ ├── quality__005_006_0_bilinear.webp │ │ ├── quality__005_006_0_lanczos.webp │ │ ├── quality__i6.bilinear.webp │ │ ├── quality__i6.cubic.webp │ │ ├── quality__i6.lanczos.webp │ │ ├── quality__resampling-overview.webp │ │ ├── quick-start__layers.png │ │ ├── relief__aerial.webp │ │ ├── relief__base.webp │ │ ├── relief__darken.webp │ │ └── relief__lighten.webp │ ├── texture-shade.md │ └── xyz-projection.md ├── quick-start.md ├── static │ ├── basemaps-service.png │ ├── tile-resize.png │ └── workflow-run.png └── user-guide │ ├── _get-started.md │ ├── api-documentation.md │ ├── swagger-api.json │ ├── technical-documentation.md │ └── use-in-esri-software │ ├── README.md │ ├── how-to-add-emergency-imagery-to-arcgis │ ├── README.md │ └── static │ │ └── emergency-response-group.png │ ├── how-to-add-individual-linz-basemaps-layers-to-arcgis │ ├── README.md │ └── static │ │ ├── basemaps-menu-button.png │ │ ├── copy-url-apikey.png │ │ ├── custom-parameters.png │ │ ├── layer-search.png │ │ ├── layer-selector.png │ │ └── move-apikey-info.png │ ├── how-to-add-linz-aerial-imagery-basemaps-to-arcgis-online │ ├── README.md │ └── static │ │ ├── step-1-agol-map-viewer.png │ │ ├── step-2-layers-panel.png │ │ ├── step-3-browse-layers-option.png │ │ ├── step-4-agol-option.png │ │ ├── step-5-search-box.png │ │ └── step-6-desired-layer-on-map.png │ └── how-to-add-linz-basemaps-to-arcgis-online-basemaps │ ├── README.md │ └── static │ ├── LINZ-basemaps-group.png │ └── basemaps-in-esri-chooser.png ├── lerna.json ├── mkdocs.yml ├── package-lock.json ├── package.json ├── packages ├── __tests__ │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── assert.ts │ │ ├── index.ts │ │ ├── rounding.ts │ │ └── test.tiff.ts │ ├── static │ │ ├── abel-tasman-and-golden-bay_2016_dem_1m_BP25_10000_0404.tiff │ │ ├── generate-tiff.sh │ │ ├── rgba8.google.png │ │ ├── rgba8.google.tiff │ │ ├── rgba8.nztm2000.png │ │ ├── rgba8.nztm2000.tiff │ │ └── rgba8_tiled.tiff │ └── tsconfig.json ├── _infra │ ├── .gitignore │ ├── CHANGELOG.md │ ├── README.md │ ├── cdk.json │ ├── package.json │ ├── src │ │ ├── analytics │ │ │ └── edge.analytics.ts │ │ ├── config.ts │ │ ├── deploy.env.ts │ │ ├── edge │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── parameters.ts │ │ ├── serve │ │ │ ├── db.ts │ │ │ ├── index.ts │ │ │ └── lambda.tiler.ts │ │ └── version.ts │ └── tsconfig.json ├── attribution │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── __tests__ │ │ │ ├── attr.test.ts │ │ │ └── utils.test.ts │ │ ├── attribution.index.ts │ │ ├── attribution.ts │ │ ├── index.ts │ │ └── utils.ts │ ├── tsconfig.json │ └── typedoc.json ├── bathymetry │ ├── CHANGELOG.md │ ├── README.md │ ├── images │ │ ├── bathyoutput.png │ │ └── bathyprocess.png │ ├── package.json │ ├── src │ │ ├── @types │ │ │ └── muiltihash.d.ts │ │ ├── __tests__ │ │ │ ├── hash.test.ts │ │ │ ├── stac.test.ts │ │ │ └── test-file.txt │ │ ├── bathy.maker.ts │ │ ├── file.ts │ │ ├── folder.ts │ │ ├── gdal │ │ │ ├── __tests__ │ │ │ │ └── gdal.progress.test.ts │ │ │ ├── gdal.command.ts │ │ │ ├── gdal.docker.ts │ │ │ ├── gdal.local.ts │ │ │ ├── gdal.progress.ts │ │ │ └── gdal.ts │ │ ├── hash.ts │ │ ├── index.ts │ │ ├── mapnik.ts │ │ └── stac.ts │ └── tsconfig.json ├── cli-config │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── __tests__ │ │ │ └── util.test.ts │ │ ├── bin.ts │ │ ├── cli │ │ │ ├── action.bundle.assets.ts │ │ │ ├── action.bundle.ts │ │ │ ├── action.create.config.ts │ │ │ ├── action.import.ts │ │ │ ├── config.diff.ts │ │ │ └── config.update.ts │ │ ├── index.ts │ │ └── util.ts │ ├── tsconfig.json │ └── typedoc.json ├── cli-raster │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── bin.ts │ │ ├── cogify │ │ │ ├── __test__ │ │ │ │ ├── covering.test.ts │ │ │ │ └── extract.test.ts │ │ │ ├── cli.ts │ │ │ ├── cli │ │ │ │ ├── __test__ │ │ │ │ │ ├── cli.cover.test.ts │ │ │ │ │ └── cli.topo.test.ts │ │ │ │ ├── cli.cog.ts │ │ │ │ ├── cli.cover.ts │ │ │ │ ├── cli.topo.ts │ │ │ │ └── cli.validate.ts │ │ │ ├── covering │ │ │ │ ├── covering.ts │ │ │ │ ├── cutline.ts │ │ │ │ └── tile.cover.ts │ │ │ ├── gdal │ │ │ │ ├── gdal.command.ts │ │ │ │ └── gdal.runner.ts │ │ │ ├── stac.ts │ │ │ └── topo │ │ │ │ ├── extract.ts │ │ │ │ ├── slug.ts │ │ │ │ └── stac.creation.ts │ │ ├── download.ts │ │ ├── hash.stream.ts │ │ ├── index.ts │ │ └── preset.ts │ ├── static │ │ ├── example-covering.png │ │ └── nztm-tile-index.png │ ├── tsconfig.json │ └── typedoc.json ├── cli-vector │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── schema │ │ ├── addresses.json │ │ ├── aerialways.json │ │ ├── boundaries.json │ │ ├── buildings.json │ │ ├── contours.json │ │ ├── dam_lines.json │ │ ├── ferries.json │ │ ├── land.json │ │ ├── parcel_boundaries.json │ │ ├── pier_lines.json │ │ ├── place_labels.json │ │ ├── pois.json │ │ ├── public_transport.json │ │ ├── sites.json │ │ ├── street_labels.json │ │ ├── street_polygons.json │ │ ├── streets.json │ │ ├── water_lines.json │ │ └── water_polygons.json │ ├── src │ │ ├── bin.ts │ │ ├── cli │ │ │ ├── cli.create.ts │ │ │ ├── cli.extract.ts │ │ │ └── cli.join.ts │ │ ├── extract.ts │ │ ├── generalization │ │ │ ├── generalization.ts │ │ │ └── simplify.ts │ │ ├── index.ts │ │ ├── modify │ │ │ ├── consts.ts │ │ │ ├── layers │ │ │ │ ├── __test__ │ │ │ │ │ └── contours.test.ts │ │ │ │ ├── contours.ts │ │ │ │ ├── place_labels.ts │ │ │ │ ├── pois.ts │ │ │ │ ├── public_transport.ts │ │ │ │ ├── street_labels.ts │ │ │ │ ├── streets.ts │ │ │ │ └── water_polygons.ts │ │ │ ├── modify.ts │ │ │ ├── parser.ts │ │ │ ├── schema.ts │ │ │ └── shared.ts │ │ ├── schema-loader │ │ │ ├── parser.ts │ │ │ ├── schema.loader.ts │ │ │ └── schema.ts │ │ ├── stac.ts │ │ ├── transform │ │ │ ├── covt.ts │ │ │ ├── mbtiles.to.ttiles.ts │ │ │ ├── ogr2ogr.ts │ │ │ └── tippecanoe.ts │ │ ├── types │ │ │ └── VectorGeoFeature.ts │ │ └── util.ts │ ├── tsconfig.json │ └── typedoc.json ├── cli │ ├── CHANGELOG.md │ ├── Dockerfile │ ├── README.md │ ├── __test.assets__ │ │ ├── kapiti.geojson │ │ ├── mana.geojson │ │ ├── tif1.tiff │ │ └── tif2.tiff │ ├── bin │ │ └── bmc.js │ ├── package.json │ ├── src │ │ └── cli │ │ │ ├── bin.ts │ │ │ └── index.ts │ ├── tsconfig.json │ └── typedoc.json ├── config-loader │ ├── CHANGELOG.md │ ├── package.json │ ├── src │ │ ├── index.ts │ │ └── json │ │ │ ├── __tests__ │ │ │ ├── config.loader.test.ts │ │ │ ├── tiff.config.test.ts │ │ │ └── tiff.load.test.ts │ │ │ ├── imagery.config.cache.ts │ │ │ ├── json.config.ts │ │ │ ├── log.ts │ │ │ ├── parse.provider.ts │ │ │ ├── parse.style.ts │ │ │ ├── parse.tile.set.ts │ │ │ └── tiff.config.ts │ └── tsconfig.json ├── config │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── __tests__ │ │ │ ├── layer.name.test.ts │ │ │ ├── names.ts │ │ │ └── prefix.test.ts │ │ ├── base.config.ts │ │ ├── base58.node.ts │ │ ├── base58.ts │ │ ├── color.ts │ │ ├── config │ │ │ ├── __test__ │ │ │ │ └── config.test.ts │ │ │ ├── base.ts │ │ │ ├── config.bundle.ts │ │ │ ├── imagery.ts │ │ │ ├── prefix.ts │ │ │ ├── provider.ts │ │ │ ├── tile.set.output.ts │ │ │ ├── tile.set.pipeline.ts │ │ │ ├── tile.set.ts │ │ │ └── vector.style.ts │ │ ├── index.ts │ │ ├── memory │ │ │ ├── __tests__ │ │ │ │ └── memory.config.test.ts │ │ │ └── memory.config.ts │ │ └── name.convertor.ts │ ├── tsconfig.json │ └── typedoc.json ├── geo │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── __tests__ │ │ │ ├── bounds.test.ts │ │ │ ├── epsg.test.ts │ │ │ ├── quad.key.test.ts │ │ │ ├── slug.test.ts │ │ │ └── tile.matrix.set.test.ts │ │ ├── bounds.ts │ │ ├── epsg.ts │ │ ├── formats.ts │ │ ├── index.ts │ │ ├── proj │ │ │ ├── __tests__ │ │ │ │ ├── projection.test.ts │ │ │ │ ├── projection.tile.matrix.set.test.ts │ │ │ │ └── test.util.ts │ │ │ ├── citm2000.ts │ │ │ ├── nztm2000.ts │ │ │ ├── projection.loader.ts │ │ │ ├── projection.ts │ │ │ └── tile.set.name.ts │ │ ├── quad.key.ts │ │ ├── simplify.ts │ │ ├── slug.ts │ │ ├── stac │ │ │ ├── index.ts │ │ │ └── stac.attribution.ts │ │ ├── tile.json │ │ │ └── tile.json.ts │ │ ├── tile.matrix.set.ts │ │ ├── tile.ts │ │ ├── tms │ │ │ ├── citm2000.ts │ │ │ ├── google.ts │ │ │ ├── index.ts │ │ │ └── nztm2000.ts │ │ ├── wmts │ │ │ └── wmts.ts │ │ └── xy.order.ts │ ├── tsconfig.json │ └── typedoc.json ├── lambda-analytic-cloudfront │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── __test__ │ │ │ ├── analytics.test.ts │ │ │ └── log.data.ts │ │ ├── bin.ts │ │ ├── date.ts │ │ ├── elastic.ts │ │ ├── handler.ts │ │ ├── index.ts │ │ ├── log.reader.ts │ │ ├── log.stats.ts │ │ ├── log │ │ │ ├── __test__ │ │ │ │ └── tile.url.test.ts │ │ │ ├── query.ts │ │ │ ├── referer.ts │ │ │ └── tile.url.ts │ │ └── useragent │ │ │ ├── __test__ │ │ │ └── parser.test.ts │ │ │ ├── agent.ts │ │ │ ├── agents │ │ │ ├── gis.ts │ │ │ └── programming.ts │ │ │ ├── parser.ts │ │ │ └── parser.types.ts │ ├── tsconfig.json │ └── typedoc.json ├── lambda-analytics │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── __tests__ │ │ │ ├── file.process.test.ts │ │ │ ├── index.test.ts │ │ │ └── ua.test.ts │ │ ├── file.process.ts │ │ ├── index.ts │ │ ├── stats.ts │ │ └── ua.ts │ ├── tsconfig.json │ └── typedoc.json ├── lambda-tiler │ ├── CHANGELOG.md │ ├── README.md │ ├── bundle.sh │ ├── package.json │ ├── scripts │ │ └── create.deployment.package.mjs │ ├── src │ │ ├── __tests__ │ │ │ ├── config.data.ts │ │ │ ├── index.test.ts │ │ │ ├── tile.style.json.test.ts │ │ │ ├── wmts.capability.test.ts │ │ │ └── xyz.util.ts │ │ ├── cli │ │ │ ├── render.preview.ts │ │ │ └── render.tile.ts │ │ ├── index.ts │ │ ├── routes │ │ │ ├── __tests__ │ │ │ │ ├── attribution.test.ts │ │ │ │ ├── fonts.test.ts │ │ │ │ ├── health.test.ts │ │ │ │ ├── imagery.test.ts │ │ │ │ ├── link.test.ts │ │ │ │ ├── memory.fs.ts │ │ │ │ ├── preview.index.test.ts │ │ │ │ ├── sprites.test.ts │ │ │ │ ├── tile.json.test.ts │ │ │ │ ├── tile.style.json.attribution.test.ts │ │ │ │ ├── tile.style.json.test.ts │ │ │ │ ├── wmts.test.ts │ │ │ │ └── xyz.test.ts │ │ │ ├── attribution.ts │ │ │ ├── config.ts │ │ │ ├── fonts.ts │ │ │ ├── health.ts │ │ │ ├── imagery.ts │ │ │ ├── link.ts │ │ │ ├── ping.ts │ │ │ ├── preview.index.ts │ │ │ ├── preview.ts │ │ │ ├── sprites.ts │ │ │ ├── tile.json.ts │ │ │ ├── tile.style.json.ts │ │ │ ├── tile.wmts.ts │ │ │ ├── tile.xyz.raster.ts │ │ │ ├── tile.xyz.ts │ │ │ ├── tile.xyz.vector.ts │ │ │ └── version.ts │ │ ├── util │ │ │ ├── __test__ │ │ │ │ ├── cache.test.ts │ │ │ │ ├── config.loader.test.ts │ │ │ │ ├── nztm.style.test.ts │ │ │ │ └── validate.test.ts │ │ │ ├── assets.provider.ts │ │ │ ├── config.cache.ts │ │ │ ├── config.loader.ts │ │ │ ├── cotar.serve.ts │ │ │ ├── etag.ts │ │ │ ├── nztm.style.ts │ │ │ ├── response.ts │ │ │ ├── source.cache.ts │ │ │ ├── swapping.lru.ts │ │ │ └── validate.ts │ │ └── wmts.capability.ts │ ├── static │ │ ├── expected_tile_2193_153_255_z7.png │ │ ├── expected_tile_NZTM2000Quad_30_33_z6.png │ │ └── expected_tile_WebMercatorQuad_252_156_z8.png │ ├── tsconfig.json │ └── typedoc.json ├── landing │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── scripts │ │ └── deploy.mjs │ ├── serve.mjs │ ├── src │ │ ├── __tests__ │ │ │ ├── NZTMTileLocation.png │ │ │ ├── config.debug.test.ts │ │ │ ├── config.test.ts │ │ │ ├── geojson.test.ts │ │ │ ├── map.config.test.ts │ │ │ └── tile.matrix.test.ts │ │ ├── attribution.ts │ │ ├── components │ │ │ ├── copyable.tsx │ │ │ ├── debug.tsx │ │ │ ├── feature.updates.tsx │ │ │ ├── layer.switcher.dropdown.tsx │ │ │ ├── layout.footer.tsx │ │ │ ├── layout.header.tsx │ │ │ ├── link.tsx │ │ │ ├── map.label.tsx │ │ │ ├── map.switcher.tsx │ │ │ ├── map.tsx │ │ │ └── new-features │ │ │ │ └── 3d.map.tsx │ │ ├── config.debug.ts │ │ ├── config.layer.ts │ │ ├── config.map.ts │ │ ├── config.ts │ │ ├── debug.map.ts │ │ ├── global.ts │ │ ├── index.tsx │ │ ├── tile.matrix.ts │ │ ├── url.ts │ │ └── webp.ts │ ├── static │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon.png │ │ ├── basemaps-card.jpeg │ │ ├── examples │ │ │ ├── index.html │ │ │ ├── index.leaflet.xyz.3857.html │ │ │ ├── index.maplibre.compare.3857.html │ │ │ ├── index.maplibre.elevation.3857.html │ │ │ ├── index.maplibre.opacity.3857.html │ │ │ ├── index.maplibre.vector.3857.html │ │ │ ├── index.openlayers.attribution.wmts.3857.html │ │ │ ├── index.openlayers.wmts.3857.html │ │ │ └── index.openlayers.xyz.3857.html │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon.ico │ │ ├── index.css │ │ ├── index.html │ │ └── json-schema │ │ │ └── stac-basemaps-extension │ │ │ └── 1.0 │ │ │ └── schema.json │ ├── tsconfig.json │ └── typedoc.json ├── linzjs-docker-command │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── __tests__ │ │ │ └── example.ts │ │ ├── command.execution.ts │ │ ├── command.ts │ │ ├── execution │ │ │ ├── __tests__ │ │ │ │ ├── execute.docker.test.ts │ │ │ │ └── execute.local.test.ts │ │ │ ├── execute.docker.ts │ │ │ ├── execute.local.ts │ │ │ ├── execute.result.ts │ │ │ └── execute.ts │ │ └── index.ts │ ├── tsconfig.json │ └── typedoc.json ├── linzjs-geojson │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── @types │ │ │ └── lineclip.d.ts │ │ ├── __tests__ │ │ │ ├── bbox.test.ts │ │ │ ├── construct.test.ts │ │ │ └── wgs84.test.ts │ │ ├── bbox.ts │ │ ├── construct.ts │ │ ├── index.ts │ │ ├── multipolygon │ │ │ ├── __tests__ │ │ │ │ ├── area.test.ts │ │ │ │ ├── clipped.test.ts │ │ │ │ └── convert.test.ts │ │ │ ├── area.ts │ │ │ ├── clipped.ts │ │ │ └── convert.ts │ │ ├── types.ts │ │ ├── util │ │ │ ├── __test__ │ │ │ │ ├── iterate.test.ts │ │ │ │ └── truncate.test.ts │ │ │ ├── iterate.ts │ │ │ └── truncate.ts │ │ └── wgs84.ts │ ├── tsconfig.json │ └── typedoc.json ├── linzjs-metrics │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── __tests__ │ │ │ └── metrics.test.ts │ │ └── metrics.ts │ ├── tsconfig.json │ └── typedoc.json ├── server │ ├── .gitignore │ ├── CHANGELOG.md │ ├── Dockerfile │ ├── README.md │ ├── package.json │ ├── src │ │ ├── bin.ts │ │ ├── cli.ts │ │ ├── config.ts │ │ ├── index.ts │ │ ├── route.layers.ts │ │ └── server.ts │ ├── tsconfig.json │ └── typedoc.json ├── shared │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── __tests__ │ │ │ ├── api.test.ts │ │ │ ├── env.test.ts │ │ │ ├── fqdn.test.ts │ │ │ ├── url.test.ts │ │ │ ├── util.test.ts │ │ │ └── vdom.test.ts │ │ ├── api.ts │ │ ├── cli │ │ │ ├── __tests__ │ │ │ │ └── git.tag.test.ts │ │ │ ├── argo.ts │ │ │ ├── base.ts │ │ │ ├── git.tag.ts │ │ │ ├── info.ts │ │ │ ├── log.ts │ │ │ └── parsers.ts │ │ ├── config.ts │ │ ├── const.ts │ │ ├── dynamo │ │ │ ├── __tests__ │ │ │ │ ├── config.dynamo.test.ts │ │ │ │ ├── config.imagery.test.ts │ │ │ │ └── config.provider.test.ts │ │ │ ├── dynamo.config.base.ts │ │ │ ├── dynamo.config.cached.ts │ │ │ └── dynamo.config.ts │ │ ├── file.system.middleware.ts │ │ ├── file.system.ts │ │ ├── imagery.url.ts │ │ ├── index.ts │ │ ├── log.ts │ │ ├── logger.fatal.error.ts │ │ ├── scripts │ │ │ └── debug.tile.matrix.ts │ │ ├── url.ts │ │ ├── util.ts │ │ └── vdom.ts │ ├── tsconfig.json │ └── typedoc.json ├── smoke │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── base.ts │ │ ├── fonts.test.ts │ │ ├── health.test.ts │ │ ├── index.test.ts │ │ ├── tile.test.ts │ │ └── wmts.test.ts │ ├── tsconfig.json │ └── typedoc.json ├── sprites │ ├── CHANGELOG.md │ ├── README.md │ ├── bin │ │ └── basemaps-sprites.mjs │ ├── package.json │ ├── src │ │ ├── __test__ │ │ │ ├── readme.test.ts │ │ │ └── sprite.test.ts │ │ ├── cli.ts │ │ ├── fs.ts │ │ ├── index.ts │ │ └── sprites.ts │ ├── static │ │ └── sprites │ │ │ ├── airport_aerodrome_pnt_fill.svg │ │ │ ├── circle.png │ │ │ ├── embankment_no_gap_cl_thick_wide.svg │ │ │ ├── mast_pnt.svg │ │ │ ├── openstreetmap_poi_bus.svg │ │ │ ├── openstreetmap_poi_plane.svg │ │ │ ├── openstreetmap_shield_ca_qc_a_2.svg │ │ │ └── openstreetmap_shield_kr_expressway_2.svg │ ├── tsconfig.json │ └── typedoc.json ├── tiler-sharp │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── __tests__ │ │ │ ├── tile.benchmark.ts │ │ │ └── tile.creation.test.ts │ │ ├── index.ts │ │ └── pipeline │ │ │ ├── __tests__ │ │ │ ├── pipeline.color.ramp.test.ts │ │ │ ├── pipeline.resize.test.ts │ │ │ └── terrain.rgb.test.ts │ │ │ ├── colorize │ │ │ ├── color.ramp.ts │ │ │ ├── colorize.ts │ │ │ └── grey.scale.ts │ │ │ ├── decompressor.lerc.ts │ │ │ ├── decompressor.ts │ │ │ ├── pipeline.color.ramp.ts │ │ │ ├── pipeline.resize.ts │ │ │ ├── pipeline.terrain.rgb.ts │ │ │ └── pipelines.ts │ ├── static │ │ ├── expected_tile_2193_256x256_0_1_z0.png │ │ ├── expected_tile_2193_256x256_0_2_z0.png │ │ ├── expected_tile_2193_256x256_1_1_z0.png │ │ ├── expected_tile_2193_256x256_1_2_z0.png │ │ ├── expected_tile_2193_256x256_4_7_z2.png │ │ ├── expected_tile_2193_256x256_4_8_z2.png │ │ ├── expected_tile_2193_256x256_5_8_z2.png │ │ ├── expected_tile_2193_256x256_6_8_z2.png │ │ ├── expected_tile_3857_256x256_0_0_z1.png │ │ ├── expected_tile_3857_256x256_0_1_z1.png │ │ ├── expected_tile_3857_256x256_1024_1024_z11.png │ │ ├── expected_tile_3857_256x256_128_128_z8.png │ │ ├── expected_tile_3857_256x256_1_0_z1.png │ │ ├── expected_tile_3857_256x256_1_1_z1.png │ │ ├── expected_tile_3857_256x256_2048_2048_z12.png │ │ ├── expected_tile_3857_256x256_256_256_z9.png │ │ ├── expected_tile_3857_256x256_2_2_z2.png │ │ ├── expected_tile_3857_256x256_4096_4096_z13.png │ │ ├── expected_tile_3857_256x256_4_4_z3.png │ │ ├── expected_tile_3857_256x256_4_5_z3.png │ │ ├── expected_tile_3857_256x256_512_512_z10.png │ │ ├── expected_tile_3857_256x256_5_4_z3.png │ │ ├── expected_tile_3857_256x256_5_5_z3.png │ │ └── expected_tile_3857_256x256_64_64_z7.png │ ├── tsconfig.json │ └── typedoc.json └── tiler │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ ├── __tests__ │ │ └── tiler.test.ts │ ├── index.ts │ ├── raster.ts │ └── tiler.ts │ ├── tsconfig.json │ └── typedoc.json ├── scripts ├── bundle.mjs ├── detect.unlinked.dep.mjs ├── file.util.mjs └── version.bump.sh ├── tsconfig.base.json ├── tsconfig.json └── typedoc.json /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | const cfg = { 2 | ...require('@linzjs/style/.eslintrc.cjs'), 3 | }; 4 | 5 | cfg.rules['no-console'] = ['error', { allow: ['warn', 'error'] }]; 6 | 7 | // Disable no floating promises in tests until https://github.com/nodejs/node/issues/51292 is solved 8 | const testOverrides = cfg.overrides.find((ovr) => ovr.files.find((f) => f.includes('.test.ts'))); 9 | testOverrides.rules['@typescript-eslint/no-floating-promises'] = 'off'; 10 | 11 | // Allow console.log in tests and scripts 12 | cfg.overrides.push({ 13 | files: ['**/*.mjs', '**/__tests__/**/*.ts'], 14 | rules: { 'no-console': 'off', '@typescript-eslint/no-explicit-any': 'off' }, 15 | }); 16 | 17 | // Disable required importing of react as typescript does this for us 18 | cfg.overrides.push({ 19 | files: '**/*.tsx', 20 | rules: { 'react/react-in-jsx-scope': 'off' }, 21 | }); 22 | 23 | // cfg.parserOptions.ecmaVersion = 2020; 24 | 25 | module.exports = cfg; 26 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @linz/proj-basemaps-dev 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | labels: bug 5 | --- 6 | 7 | ### Bug Description 8 | 9 | A clear and concise description of what the bug is and what you expected to happen. 10 | 11 | #### Steps to Reproduce 12 | 13 | Steps to reproduce the behavior: 14 | 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | #### Desktop 21 | 22 | - Environment: [e.g. Windows / DaaS / Ubuntu] 23 | - Relevant Software Versions [e.g. QGIS 2.18.21] 24 | 25 | #### Screenshots 26 | 27 | If applicable, add screenshots to help explain your problem. 28 | 29 | **Add an _Assignee_, _Milestone_, _Release_ and any relevant _Labels_.** 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | labels: enhancement 5 | --- 6 | 7 | ### User Story 8 | 9 | In order to [accomplish goal] as a [role] I want [capability] 10 | (optional: instead of [existing behaviour]). 11 | 12 | #### Acceptance Criteria 13 | 14 | - [ ] ... 15 | - [ ] ... 16 | - [ ] ... 17 | 18 | **Add an _Assignee_, _Milestone_, _Release_ and any relevant _Labels_.** 19 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: 'github-actions' 4 | directory: '/' 5 | schedule: 6 | interval: daily 7 | - package-ecosystem: npm 8 | directory: '/' 9 | schedule: 10 | interval: daily 11 | ignore: 12 | - dependency-name: '@aws-cdk/*' 13 | - dependency-name: 'aws-cdk' 14 | - dependency-name: 'aws-sdk' 15 | open-pull-requests-limit: 10 16 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ### Motivation 2 | 3 | 4 | 5 | ### Modifications 6 | 7 | 8 | 9 | 10 | 11 | ### Verification 12 | 13 | 14 | -------------------------------------------------------------------------------- /.github/workflows/pull-request.yml: -------------------------------------------------------------------------------- 1 | name: Pull Request lint 2 | 3 | on: 4 | pull_request: 5 | types: ['opened', 'edited', 'reopened', 'synchronize'] 6 | 7 | jobs: 8 | lint: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: linz/action-pull-request-lint@v1 12 | with: 13 | conventional: 'error' # require conventional pull request title (default: "error" options: "error", "warn", "off") 14 | 15 | jira: 'warn' # Require JIRA ticket references (default: "warn", options: "error", "warn", "off") 16 | jira-projects: 'BM,TDE' # optional list of jira projects 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | tsconfig.tsbuildinfo 4 | dist 5 | .cache 6 | cdk.out 7 | cdk.context.json 8 | *.tgz -------------------------------------------------------------------------------- /.hyperfine.json: -------------------------------------------------------------------------------- 1 | [ 2 | { "name": "Tile 256x256", "command": "node packages/tiler-sharp/build/__tests__/tile.benchmark.js 256" }, 3 | { "name": "Tile 512x512", "command": "node packages/tiler-sharp/build/__tests__/tile.benchmark.js 512" } 4 | ] -------------------------------------------------------------------------------- /.prettierrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | ...require('@linzjs/style/.prettierrc.cjs'), 3 | }; 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Crown copyright (c), Toitū Te Whenua Land Information New Zealand on behalf of the New Zealand Government. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Basemaps 2 | 3 | [![Build Status](https://github.com/linz/basemaps/workflows/Build/badge.svg)](https://github.com/linz/basemaps/actions) 4 | [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/linz/basemaps/blob/master/LICENSE) 5 | 6 | 7 | A digital basemap provides a consistent background detail necessary to orient location and add to aesthetic appeal. Basemaps can be made up of streets, parcels, boundaries (country, regional, and city boundaries), shaded relief of a digital elevation model, waterways, hydrography, aerial and satellite imagery. Basemaps can be used as desktop, website or mobile phone application components, or as a 3rd party layers within a GIS or desktop mapping application. 8 | 9 | 10 | ## License 11 | 12 | This system is licensed under the MIT License, except where otherwise specified. See the [LICENSE](https://github.com/linz/basemaps/blob/master/LICENSE) file for more details. 13 | 14 | ## Development 15 | 16 | For background information about the basemaps service and its development see [Documentation](./docs/README.md) 17 | 18 | ## Building 19 | 20 | For building and contributing guide see [Contributing](./CONTRIBUTING.md) 21 | 22 | ## Changes 23 | 24 | For a history of basemaps changes see the [Changelog](./CHANGELOG.md) -------------------------------------------------------------------------------- /docs/developer-guide/API/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /docs/developer-guide/README.md: -------------------------------------------------------------------------------- 1 | # Developer guide 2 | 3 | Documentation of the Basemaps codebase for developers 4 | 5 | - [How to run `basemaps` locally](./run-basemaps-locally.md) 6 | - Contributing guidelines 7 | - TypeDoc API docs 8 | 9 | ## Creating and updating docs 10 | 11 | The best way to run the docs locally is to use the same container as the CI process 12 | 13 | To start the container in a local dev mode which creates a server on [http://localhost:8000](http://localhost:8000) 14 | 15 | ```bash 16 | docker run --rm \ 17 | -v $PWD:/docs \ 18 | -p 8000:8000 \ 19 | squidfunk/mkdocs-material:9.4 serve -a 0.0.0.0:8000 20 | ``` 21 | 22 | As it is run inside of a container the additional `-a` to allow external connections to access it. 23 | -------------------------------------------------------------------------------- /docs/developer-guide/server-methods/serve-basemaps-with-collection-of-geotiff-files.md: -------------------------------------------------------------------------------- 1 | # Serve `basemaps` using a collection of GeoTIFF files 2 | 3 | This guide shows you how to configure and run the **basemaps/server** package using a collection of GeoTIFF files. 4 | 5 | ## Configure the `basemaps/server` package 6 | 7 | The **basemaps/server** package requires a collection of GeoTIFF files from which it can serve the **basemaps** system. 8 | 9 | !!! abstract "Path" 10 | 11 | Make a note of the following path: 12 | 13 | === "`IMAGERY_DIR`" 14 | 15 | The path to the root folder of your GeoTIFF file collection. 16 | 17 | ```bash 18 | $IMAGERY_DIR = {path_to_imagery_directory} 19 | ``` 20 | 21 | ## Run the `basemaps/server` package 22 | 23 | !!! abstract "Path" 24 | 25 | The **basemaps/server** package provides a default function to serve the **basemaps** system. Note the following path: 26 | 27 | === "`BM_SERVER_BUILD`" 28 | 29 | The path to the **build** folder of the **basemaps/server** package. 30 | 31 | ```bash 32 | $BM_SERVER_BUILD = $BM_REPO/packages/server/build 33 | ``` 34 | 35 | ### Command 36 | 37 | Use the following command to run the **Basemaps** system: 38 | 39 | ```bash 40 | node $BM_SERVER_BUILD/bin.js $IMAGERY_DIR 41 | ``` 42 | -------------------------------------------------------------------------------- /docs/examples/_overview.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | [Use leaflet render raster map](./leaflet.xyz.3857.md) 4 | 5 | 6 | 7 | [Use Maplibre adjust opacity of the map](./maplibre.opacity.3857.md) 8 | 9 | 10 | 11 | [Use Maplibre to load vector tiles](./maplibre.vector.3857.md) 12 | 13 | 14 | -------------------------------------------------------------------------------- /docs/examples/index.leaflet.xyz.3857.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Leaflet WGS84 Basemaps Demo 6 | 7 | 12 | 19 | 20 | 21 | 22 |
23 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /docs/examples/leaflet.xyz.3857.md: -------------------------------------------------------------------------------- 1 | # Leaflet Raster Map 2 | 3 | Use leaflet render raster map 4 | 5 | 6 | 7 | ```html 8 | --8<-- "examples/index.leaflet.xyz.3857.html" 9 | ``` 10 | -------------------------------------------------------------------------------- /docs/examples/maplibre.opacity.3857.md: -------------------------------------------------------------------------------- 1 | # Maplibre Opacity Slider 2 | 3 | Use Maplibre adjust opacity of the map 4 | 5 | 6 | 7 | ```html 8 | --8<-- "examples/index.maplibre.opacity.3857.html" 9 | ``` 10 | -------------------------------------------------------------------------------- /docs/examples/maplibre.vector.3857.md: -------------------------------------------------------------------------------- 1 | # Maplibre Vector Map 2 | 3 | Use Maplibre to load vector tiles 4 | 5 | 6 | 7 | ```html 8 | --8<-- "examples/index.maplibre.vector.3857.html" 9 | ``` 10 | -------------------------------------------------------------------------------- /docs/index.css: -------------------------------------------------------------------------------- 1 | .md-header { 2 | background: linear-gradient(70deg, #00425d 12%, #007198); 3 | } 4 | -------------------------------------------------------------------------------- /docs/operator-guide/README.md: -------------------------------------------------------------------------------- 1 | # Basemaps operator guide 2 | 3 | How to deploy your own instance of Basemaps 4 | 5 | - How to run basemaps on your own imagery 6 | - Simple CLI examples on how to serve tiffs from docker 7 | - How to process imagery into more efficent formats 8 | - Complex TIFF examples 9 | -------------------------------------------------------------------------------- /docs/operator-guide/static/2023-06-26-gisborne-2023.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/docs/operator-guide/static/2023-06-26-gisborne-2023.png -------------------------------------------------------------------------------- /docs/operator-guide/static/projection_beehive_fakenztm2000.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/docs/operator-guide/static/projection_beehive_fakenztm2000.webp -------------------------------------------------------------------------------- /docs/operator-guide/static/projection_beehive_nztm2000quad.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/docs/operator-guide/static/projection_beehive_nztm2000quad.webp -------------------------------------------------------------------------------- /docs/operator-guide/static/projection_nztm2000quad_0_0_0.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/docs/operator-guide/static/projection_nztm2000quad_0_0_0.webp -------------------------------------------------------------------------------- /docs/operator-guide/static/quality__005_006_0_bilinear.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/docs/operator-guide/static/quality__005_006_0_bilinear.webp -------------------------------------------------------------------------------- /docs/operator-guide/static/quality__005_006_0_lanczos.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/docs/operator-guide/static/quality__005_006_0_lanczos.webp -------------------------------------------------------------------------------- /docs/operator-guide/static/quality__i6.bilinear.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/docs/operator-guide/static/quality__i6.bilinear.webp -------------------------------------------------------------------------------- /docs/operator-guide/static/quality__i6.cubic.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/docs/operator-guide/static/quality__i6.cubic.webp -------------------------------------------------------------------------------- /docs/operator-guide/static/quality__i6.lanczos.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/docs/operator-guide/static/quality__i6.lanczos.webp -------------------------------------------------------------------------------- /docs/operator-guide/static/quality__resampling-overview.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/docs/operator-guide/static/quality__resampling-overview.webp -------------------------------------------------------------------------------- /docs/operator-guide/static/quick-start__layers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/docs/operator-guide/static/quick-start__layers.png -------------------------------------------------------------------------------- /docs/operator-guide/static/relief__aerial.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/docs/operator-guide/static/relief__aerial.webp -------------------------------------------------------------------------------- /docs/operator-guide/static/relief__base.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/docs/operator-guide/static/relief__base.webp -------------------------------------------------------------------------------- /docs/operator-guide/static/relief__darken.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/docs/operator-guide/static/relief__darken.webp -------------------------------------------------------------------------------- /docs/operator-guide/static/relief__lighten.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/docs/operator-guide/static/relief__lighten.webp -------------------------------------------------------------------------------- /docs/quick-start.md: -------------------------------------------------------------------------------- 1 | # Quick start guide 2 | 3 | This document is broken down into three user types: User, Operator and Developer. 4 | 5 | - **User** wants to use the basemaps service running on [https://basemaps.linz.govt.nz](https://basemaps.linz.govt.nz) 6 | - **Operator** wants to use `@basemaps/server` or `@linzjs/lambda-tiler` on their own imagery 7 | - **Developer** wants to modify the linz/basemaps source code to meet their needs. 8 | 9 | There are three separate quick start guides for these users. 10 | 11 | - [User Quick Start](./user-guide/quick-start.md) 12 | - [Operator Quick Start](./operator-guide/quick-start.md) 13 | - [Developer Quick Start](./developer-guide/quick-start.md) 14 | -------------------------------------------------------------------------------- /docs/static/basemaps-service.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/docs/static/basemaps-service.png -------------------------------------------------------------------------------- /docs/static/tile-resize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/docs/static/tile-resize.png -------------------------------------------------------------------------------- /docs/static/workflow-run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/docs/static/workflow-run.png -------------------------------------------------------------------------------- /docs/user-guide/api-documentation.md: -------------------------------------------------------------------------------- 1 | # API documentation 2 | 3 | This page provide examples of Basemaps APIs that allow you to programmatically access LINZ map tile services to integrate into your mobile, web. 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/user-guide/use-in-esri-software/README.md: -------------------------------------------------------------------------------- 1 | # Use in Esri software 2 | 3 | This page provides a step-by-step guide to several common ways LINZ Basemaps APIs can be used within Esri software, including ArcGIS Online (AGOL) and Enterprise. 4 | 5 | - [How to add LINZ Aerial Imagery Basemaps to ArcGIS Online][1] 6 | - [How to add imagery captured during an emergency to ArcGIS][2] 7 | - [How to add an individual basemap or imagery layer from LINZ Basemaps to ArcGIS][3] 8 | - [How to add LINZ Imagery Basemaps to your AGOL basemaps][4] 9 | 10 | [1]: how-to-add-linz-aerial-imagery-basemaps-to-arcgis-online/README.md 11 | [2]: how-to-add-emergency-imagery-to-arcgis/README.md 12 | [3]: how-to-add-individual-linz-basemaps-layers-to-arcgis/README.md 13 | [4]: how-to-add-linz-basemaps-to-arcgis-online-basemaps/README.md 14 | -------------------------------------------------------------------------------- /docs/user-guide/use-in-esri-software/how-to-add-emergency-imagery-to-arcgis/README.md: -------------------------------------------------------------------------------- 1 | # How to add imagery captured during an emergency to ArcGIS 2 | 3 | During an emergency response, LINZ will create an ArcGIS Online item for each imagery dataset published on LINZ Basemaps. The ArcGIS Online item will make it easier to discover and add the imagery to ArcGIS Online. 4 | 5 | Either search ArcGIS Online for LINZ imagery emergency or view all available emergency response imagery from the [LINZ Emergency Response Imagery Collection AGOL Group](https://linz.maps.arcgis.com/home/group.html?id=b71aee8952d84164a0ea9c06d5c988fd). 6 | 7 | Note the imagery is hosted from LINZ Imagery Basemaps in Web Map Tile Service (WMTS) format. 8 | 9 | !!! tip 10 | 11 | If you join the [LINZ Emergency Response Imagery Collection AGOL Group](https://linz.maps.arcgis.com/home/group.html?id=b71aee8952d84164a0ea9c06d5c988fd), it will be easier to find response imagery in future. 12 | 13 | ![Collection of emergency response layers](static/emergency-response-group.png) 14 | -------------------------------------------------------------------------------- /docs/user-guide/use-in-esri-software/how-to-add-emergency-imagery-to-arcgis/static/emergency-response-group.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/docs/user-guide/use-in-esri-software/how-to-add-emergency-imagery-to-arcgis/static/emergency-response-group.png -------------------------------------------------------------------------------- /docs/user-guide/use-in-esri-software/how-to-add-individual-linz-basemaps-layers-to-arcgis/static/basemaps-menu-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/docs/user-guide/use-in-esri-software/how-to-add-individual-linz-basemaps-layers-to-arcgis/static/basemaps-menu-button.png -------------------------------------------------------------------------------- /docs/user-guide/use-in-esri-software/how-to-add-individual-linz-basemaps-layers-to-arcgis/static/copy-url-apikey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/docs/user-guide/use-in-esri-software/how-to-add-individual-linz-basemaps-layers-to-arcgis/static/copy-url-apikey.png -------------------------------------------------------------------------------- /docs/user-guide/use-in-esri-software/how-to-add-individual-linz-basemaps-layers-to-arcgis/static/custom-parameters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/docs/user-guide/use-in-esri-software/how-to-add-individual-linz-basemaps-layers-to-arcgis/static/custom-parameters.png -------------------------------------------------------------------------------- /docs/user-guide/use-in-esri-software/how-to-add-individual-linz-basemaps-layers-to-arcgis/static/layer-search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/docs/user-guide/use-in-esri-software/how-to-add-individual-linz-basemaps-layers-to-arcgis/static/layer-search.png -------------------------------------------------------------------------------- /docs/user-guide/use-in-esri-software/how-to-add-individual-linz-basemaps-layers-to-arcgis/static/layer-selector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/docs/user-guide/use-in-esri-software/how-to-add-individual-linz-basemaps-layers-to-arcgis/static/layer-selector.png -------------------------------------------------------------------------------- /docs/user-guide/use-in-esri-software/how-to-add-individual-linz-basemaps-layers-to-arcgis/static/move-apikey-info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/docs/user-guide/use-in-esri-software/how-to-add-individual-linz-basemaps-layers-to-arcgis/static/move-apikey-info.png -------------------------------------------------------------------------------- /docs/user-guide/use-in-esri-software/how-to-add-linz-aerial-imagery-basemaps-to-arcgis-online/README.md: -------------------------------------------------------------------------------- 1 | # How to add LINZ Aerial Imagery Basemaps to ArcGIS Online 2 | 3 | 1. Open the [ArcGIS Online Map Viewer](https://www.arcgis.com/apps/mapviewer/index.html) in a web browser. 4 | 5 | ![ArcGIS Online Map Viewer](static/step-1-agol-map-viewer.png) 6 | 7 | 2. Open the **`Layers`** panel. 8 | 9 | ![Layers panel](static/step-2-layers-panel.png) 10 | 11 | 3. From the dropdown, select the **`Browse layers`** option. 12 | 13 | ![Browse layers option](static/step-3-browse-layers-option.png) 14 | 15 | 4. From the dropdown, select the **`ArcGIS Online`** option. 16 | 17 | ![ArcGIS Online option](static/step-4-agol-option.png) 18 | 19 | 5. Type **`LINZ Aerial Imagery Basemap`** in the search box. You should see two layers in particular: 20 | 21 | 1. `LINZ Aerial Imagery Basemap - Web Mercator` 22 | 2. `LINZ Aerial Imagery Basemap - NZTM` 23 | 24 | The two layers are unique by projection. Click the **`+ Add`** button for the desired layer. 25 | 26 | ![Search box](static/step-5-search-box.png) 27 | 28 | 6. You should now see the desired **`LINZ Aerial Imagery Basemap`** layer on the map. 29 | 30 | ![Desired layer on map](static/step-6-desired-layer-on-map.png) 31 | -------------------------------------------------------------------------------- /docs/user-guide/use-in-esri-software/how-to-add-linz-aerial-imagery-basemaps-to-arcgis-online/static/step-1-agol-map-viewer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/docs/user-guide/use-in-esri-software/how-to-add-linz-aerial-imagery-basemaps-to-arcgis-online/static/step-1-agol-map-viewer.png -------------------------------------------------------------------------------- /docs/user-guide/use-in-esri-software/how-to-add-linz-aerial-imagery-basemaps-to-arcgis-online/static/step-2-layers-panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/docs/user-guide/use-in-esri-software/how-to-add-linz-aerial-imagery-basemaps-to-arcgis-online/static/step-2-layers-panel.png -------------------------------------------------------------------------------- /docs/user-guide/use-in-esri-software/how-to-add-linz-aerial-imagery-basemaps-to-arcgis-online/static/step-3-browse-layers-option.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/docs/user-guide/use-in-esri-software/how-to-add-linz-aerial-imagery-basemaps-to-arcgis-online/static/step-3-browse-layers-option.png -------------------------------------------------------------------------------- /docs/user-guide/use-in-esri-software/how-to-add-linz-aerial-imagery-basemaps-to-arcgis-online/static/step-4-agol-option.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/docs/user-guide/use-in-esri-software/how-to-add-linz-aerial-imagery-basemaps-to-arcgis-online/static/step-4-agol-option.png -------------------------------------------------------------------------------- /docs/user-guide/use-in-esri-software/how-to-add-linz-aerial-imagery-basemaps-to-arcgis-online/static/step-5-search-box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/docs/user-guide/use-in-esri-software/how-to-add-linz-aerial-imagery-basemaps-to-arcgis-online/static/step-5-search-box.png -------------------------------------------------------------------------------- /docs/user-guide/use-in-esri-software/how-to-add-linz-aerial-imagery-basemaps-to-arcgis-online/static/step-6-desired-layer-on-map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/docs/user-guide/use-in-esri-software/how-to-add-linz-aerial-imagery-basemaps-to-arcgis-online/static/step-6-desired-layer-on-map.png -------------------------------------------------------------------------------- /docs/user-guide/use-in-esri-software/how-to-add-linz-basemaps-to-arcgis-online-basemaps/README.md: -------------------------------------------------------------------------------- 1 | # How to add LINZ Imagery Basemaps to your AGOL basemaps 2 | 3 | ![LINZ Basemaps in Esri's basemap chooser](static/basemaps-in-esri-chooser.png) 4 | 5 | To change the basemap options for your organisation to offer both Esri, Eagle and LINZ basemaps: 6 | 7 | 1. Sign in to ArcGIS Online as a role with ArcGIS Administrator privileges 8 | 9 | 2. Join the [NZ Basemaps Group](https://arcgis.com/home/group.html?id=4033cd7bf65a443cbaf7e1cae0e76f59) 10 | 11 | 3. In ArcGIS Online, open Organization > Settings > Map 12 | 13 | 4. Under Basemap Gallery, click the Edit button to open the Group drop-down menu and choose NZ Basemaps Group 14 | 15 | ![Join the LINZ Basemaps group ](static/LINZ-basemaps-group.png) 16 | -------------------------------------------------------------------------------- /docs/user-guide/use-in-esri-software/how-to-add-linz-basemaps-to-arcgis-online-basemaps/static/LINZ-basemaps-group.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/docs/user-guide/use-in-esri-software/how-to-add-linz-basemaps-to-arcgis-online-basemaps/static/LINZ-basemaps-group.png -------------------------------------------------------------------------------- /docs/user-guide/use-in-esri-software/how-to-add-linz-basemaps-to-arcgis-online-basemaps/static/basemaps-in-esri-chooser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/docs/user-guide/use-in-esri-software/how-to-add-linz-basemaps-to-arcgis-online-basemaps/static/basemaps-in-esri-chooser.png -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "useYarn": true, 3 | "packages": ["packages/*"], 4 | "command": { 5 | "version": { 6 | "yes": true, 7 | "conventionalCommits": true 8 | } 9 | }, 10 | "version": "8.1.0" 11 | } 12 | -------------------------------------------------------------------------------- /packages/__tests__/README.md: -------------------------------------------------------------------------------- 1 | # @basemaps/test 2 | 3 | Testing utilities and assets 4 | 5 | ## Approx equal 6 | 7 | ```typescript 8 | import { approxEqual } from '@basemaps/test'; 9 | 10 | o('should be near', () => { 11 | approxEqual(1, 2, 'one two'); // `one two (1 vs 2) should be less than 0.001` 12 | }); 13 | ``` 14 | 15 | ## TestTiffs 16 | 17 | Two RGB Testing tiffs are provided for Google (epsg:3857) and NZTM2000 (epgs:2193) projections 18 | 19 | ```typescript 20 | import { TestTiff } from '@basemaps/test'; 21 | 22 | // Create a new CogTiff for each projection 23 | const google = TestTiff.Google; 24 | const nztm2000 = TestTiff.Nztm2000; 25 | 26 | // They need to be initialized 27 | await Promise.all([google.init(), nztm2000.init()]); 28 | ``` 29 | 30 | ### rgba8.google.tiff 31 | 32 | ![Google](./static/rgba8.google.png) 33 | 34 | ### rgba8.nztm.tiff 35 | 36 | Due to NZTM2000 being a weird tiling scheme and z0 having 2x4 with half the tiles being outside of the extent, this tiff is centered around tiles: 0.5, 1.5 -> 1.5, 2.5 37 | 38 | ![NZTM](./static/rgba8.nztm2000.png) 39 | -------------------------------------------------------------------------------- /packages/__tests__/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@basemaps/test", 3 | "version": "8.0.0", 4 | "private": true, 5 | "main": "./build/index.js", 6 | "types": "./build/index.d.ts", 7 | "repository": "git@github.com:linz/basemaps.git", 8 | "author": { 9 | "name": "Land Information New Zealand", 10 | "url": "https://linz.govt.nz", 11 | "organization": true 12 | }, 13 | "type": "module", 14 | "engines": { 15 | "node": ">=16.0.0" 16 | }, 17 | "license": "MIT", 18 | "scripts": {} 19 | } 20 | -------------------------------------------------------------------------------- /packages/__tests__/src/index.ts: -------------------------------------------------------------------------------- 1 | export { Approx } from './assert.js'; 2 | export { TestTiff } from './test.tiff.js'; 3 | -------------------------------------------------------------------------------- /packages/__tests__/src/test.tiff.ts: -------------------------------------------------------------------------------- 1 | export const TestDataPath = new URL('../static/', import.meta.url); 2 | 3 | const TiffGooglePath = new URL('rgba8.google.tiff', TestDataPath); 4 | const TiffNztm2000Path = new URL('rgba8.nztm2000.tiff', TestDataPath); 5 | 6 | export class TestTiff { 7 | static get Nztm2000(): URL { 8 | return TiffNztm2000Path; 9 | } 10 | 11 | static get Google(): URL { 12 | return TiffGooglePath; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/__tests__/static/abel-tasman-and-golden-bay_2016_dem_1m_BP25_10000_0404.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/packages/__tests__/static/abel-tasman-and-golden-bay_2016_dem_1m_BP25_10000_0404.tiff -------------------------------------------------------------------------------- /packages/__tests__/static/generate-tiff.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # NZTM2000 has a weird tiling scheme, this creates a tiff centered around 4 | # tiles: 0.5, 1.5 -> 1.5, 2.5 5 | gdal_translate -a_srs epsg:2193 \ 6 | -a_ullr 146880 6559360 2440640 4265600 \ 7 | -of GTiff -co COMPRESS=WEBP -co WEBP_LOSSLESS=TRUE \ 8 | -co TILED=YES -co BLOCKXSIZE=16 -co BLOCKYSIZE=16 \ 9 | rgba8_tiled.tiff rgba8.nztm2000.tiff 10 | 11 | 12 | 13 | # Webmercator bounds are 20037508.3427892 x -20037508.3427892 square 14 | # So create a tiff approx half the size 15 | gdal_translate -a_srs epsg:3857 \ 16 | -a_ullr -10018754.1713946 10018754.1713946 10018754.1713946 -10018754.1713946 \ 17 | -of GTiff -co COMPRESS=WEBP -co WEBP_LOSSLESS=TRUE -co TILED=YES \ 18 | -co TILED=YES -co BLOCKXSIZE=16 -co BLOCKYSIZE=16 \ 19 | rgba8_tiled.tiff rgba8.google.tiff 20 | -------------------------------------------------------------------------------- /packages/__tests__/static/rgba8.google.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/packages/__tests__/static/rgba8.google.png -------------------------------------------------------------------------------- /packages/__tests__/static/rgba8.google.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/packages/__tests__/static/rgba8.google.tiff -------------------------------------------------------------------------------- /packages/__tests__/static/rgba8.nztm2000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/packages/__tests__/static/rgba8.nztm2000.png -------------------------------------------------------------------------------- /packages/__tests__/static/rgba8.nztm2000.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/packages/__tests__/static/rgba8.nztm2000.tiff -------------------------------------------------------------------------------- /packages/__tests__/static/rgba8_tiled.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/packages/__tests__/static/rgba8_tiled.tiff -------------------------------------------------------------------------------- /packages/__tests__/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "outDir": "./build", 5 | "rootDir": "./src" 6 | }, 7 | "include": ["src"], 8 | "references": [] 9 | } 10 | -------------------------------------------------------------------------------- /packages/_infra/.gitignore: -------------------------------------------------------------------------------- 1 | cdk.out 2 | -------------------------------------------------------------------------------- /packages/_infra/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "node build/index.js" 3 | } 4 | -------------------------------------------------------------------------------- /packages/_infra/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@basemaps/infra", 3 | "version": "8.1.0", 4 | "private": true, 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/linz/basemaps.git", 8 | "directory": "packages/_infra" 9 | }, 10 | "author": { 11 | "name": "Land Information New Zealand", 12 | "url": "https://linz.govt.nz", 13 | "organization": true 14 | }, 15 | "type": "module", 16 | "engines": { 17 | "node": ">=16.0.0" 18 | }, 19 | "license": "MIT", 20 | "scripts": { 21 | "deploy:synth": "cdk synth", 22 | "deploy:diff": "cdk diff || true", 23 | "deploy:deploy": "cdk deploy '*' -y --require-approval never", 24 | "test": "node --test" 25 | }, 26 | "devDependencies": { 27 | "@aws-sdk/client-acm": "^3.470.0", 28 | "@aws-sdk/client-cloudformation": "^3.470.0", 29 | "@basemaps/lambda-tiler": "^8.1.0", 30 | "@basemaps/shared": "^8.1.0", 31 | "@linzjs/cdk-tags": "^1.7.0", 32 | "aws-cdk": "2.162.x", 33 | "aws-cdk-lib": "2.162.x", 34 | "constructs": "^10.4.2" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/_infra/src/deploy.env.ts: -------------------------------------------------------------------------------- 1 | export const DeployEnv = { 2 | /** Default AWS accountId to use */ 3 | CdkAccount: 'CDK_DEFAULT_ACCOUNT', 4 | }; 5 | -------------------------------------------------------------------------------- /packages/_infra/src/serve/db.ts: -------------------------------------------------------------------------------- 1 | import { Const } from '@basemaps/shared'; 2 | import cdk from 'aws-cdk-lib'; 3 | import dynamoDb from 'aws-cdk-lib/aws-dynamodb'; 4 | import { Construct } from 'constructs'; 5 | 6 | export class TileMetadataTable extends Construct { 7 | public table: dynamoDb.Table; 8 | public constructor(scope: Construct, id: string) { 9 | super(scope, id); 10 | 11 | this.table = new dynamoDb.Table(this, 'TileMetadataDynamoTable', { 12 | tableName: Const.TileMetadata.TableName, 13 | billingMode: dynamoDb.BillingMode.PAY_PER_REQUEST, 14 | partitionKey: { name: 'id', type: dynamoDb.AttributeType.STRING }, 15 | pointInTimeRecovery: true, 16 | }); 17 | 18 | new cdk.CfnOutput(this, 'TileMetadataTable', { value: this.table.tableArn }); 19 | } 20 | } 21 | 22 | export const TileMetadataTableArn = { 23 | /** 24 | * get the ARN for the TileMetadata table 25 | * 26 | * @returns ARN of the TileMetadata table 27 | */ 28 | getArn(scope: cdk.Stack): string { 29 | return cdk.Arn.format( 30 | { 31 | service: 'dynamodb', 32 | region: '*', 33 | resource: 'table', 34 | resourceName: Const.TileMetadata.TableName, 35 | }, 36 | scope, 37 | ); 38 | }, 39 | }; 40 | -------------------------------------------------------------------------------- /packages/_infra/src/serve/index.ts: -------------------------------------------------------------------------------- 1 | import cdk from 'aws-cdk-lib'; 2 | import { Construct } from 'constructs'; 3 | 4 | import { ParametersServeKeys } from '../parameters.js'; 5 | import { TileMetadataTable } from './db.js'; 6 | import { LambdaTiler } from './lambda.tiler.js'; 7 | 8 | export interface ServeStackProps extends cdk.StackProps { 9 | /** Location of static files */ 10 | staticBucketName?: string; 11 | } 12 | 13 | /** 14 | * Tile serving infrastructure 15 | */ 16 | export class ServeStack extends cdk.Stack { 17 | public constructor(scope: Construct, id: string, props: ServeStackProps) { 18 | super(scope, id, props); 19 | 20 | const lambda = new LambdaTiler(this, 'LambdaTiler', { staticBucketName: props.staticBucketName }); 21 | const table = new TileMetadataTable(this, 'TileMetadata'); 22 | table.table.grantReadData(lambda.lambdaNoVpc); 23 | 24 | new cdk.CfnOutput(this, ParametersServeKeys.LambdaXyzUrl, { value: lambda.functionUrl.url }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/_infra/src/version.ts: -------------------------------------------------------------------------------- 1 | import { GitTag } from '@basemaps/shared/build/cli/git.tag.js'; 2 | 3 | export interface VersionInfo { 4 | /** Current git tag */ 5 | version: string; 6 | /** Current commit hash */ 7 | hash: string; 8 | } 9 | 10 | let versionInfo: VersionInfo | null = null; 11 | export const VersionUtil = { 12 | /** 13 | * Get version information about the current build 14 | * 15 | */ 16 | version(): VersionInfo { 17 | if (versionInfo == null) { 18 | versionInfo = GitTag(); 19 | } 20 | return versionInfo; 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /packages/_infra/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | 4 | "compilerOptions": { 5 | "outDir": "./build", 6 | "rootDir": "./src" 7 | }, 8 | "include": ["src"], 9 | "references": [{ "path": "../shared/tsconfig.json" }] 10 | } 11 | -------------------------------------------------------------------------------- /packages/attribution/README.md: -------------------------------------------------------------------------------- 1 | # @basemaps/attribution 2 | 3 | Library to determine to applicable attribution for a given extent and zoom level. 4 | 5 | ## Usage 6 | 7 | ```bash 8 | npm install @basemaps/attribution 9 | ``` 10 | 11 | ```typescript 12 | import { Attribution } from '@basemaps/attribution'; 13 | 14 | const attributions = await Attribution.load('https://basemaps.linz.govt.nz/v1/tiles/aerial/EPSG:3857/attribution.json?api=...'); 15 | 16 | // Find all imagery sets inside the following bounding box 17 | const attrList = attributions.filter([144.7377202, -45.8938181, 195.62639, -37.65336], 6); 18 | 19 | // Convert the attrubtion list to a human readable description 20 | const description = attributions.renderList(attrList); 21 | // "NZ 10m Satellite Imagery (2020-2021) & GEBCO 2020 Grid" 22 | ``` 23 | 24 | Using a CDN see https://basemaps.linz.govt.nz/examples/index.openlayers.attribution.wmts.3857.html. 25 | 26 | ## License 27 | 28 | This system is licensed under the MIT License, except where otherwise specified. See the [LICENSE](https://github.com/linz/basemaps/blob/master/LICENSE) file for more details. 29 | -------------------------------------------------------------------------------- /packages/attribution/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@basemaps/attribution", 3 | "version": "8.0.0", 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/linz/basemaps.git", 7 | "directory": "packages/attribution" 8 | }, 9 | "author": { 10 | "name": "Land Information New Zealand", 11 | "url": "https://linz.govt.nz", 12 | "organization": true 13 | }, 14 | "type": "module", 15 | "engines": { 16 | "node": ">=16.0.0" 17 | }, 18 | "license": "MIT", 19 | "main": "./build/index.js", 20 | "types": "./build/index.d.ts", 21 | "scripts": { 22 | "test": "node --test", 23 | "bundle": "../../scripts/bundle.mjs package.json" 24 | }, 25 | "publishConfig": { 26 | "access": "public" 27 | }, 28 | "files": [ 29 | "dist/", 30 | "build/" 31 | ], 32 | "dependencies": { 33 | "@basemaps/geo": "^8.0.0", 34 | "@linzjs/geojson": "^8.0.0" 35 | }, 36 | "bundle": { 37 | "entry": "src/attribution.index.ts", 38 | "outfile": "dist/attribution.js", 39 | "platform": "browser" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/attribution/src/attribution.index.ts: -------------------------------------------------------------------------------- 1 | import { Attribution } from './attribution.js'; 2 | 3 | declare global { 4 | interface Window { 5 | // Access to basemaps global 6 | BasemapsAttribution: typeof Attribution; 7 | } 8 | } 9 | 10 | window.BasemapsAttribution = Attribution; 11 | -------------------------------------------------------------------------------- /packages/attribution/src/index.ts: -------------------------------------------------------------------------------- 1 | export { Attribution } from './attribution.js'; 2 | -------------------------------------------------------------------------------- /packages/attribution/src/utils.ts: -------------------------------------------------------------------------------- 1 | import { Stac, StacProvider } from '@basemaps/geo'; 2 | 3 | export const copyright = `© ${Stac.License}`; 4 | 5 | /** 6 | * Create a licensor attribution string. 7 | * 8 | * @param providers The optional list of providers. 9 | * 10 | * @returns A copyright string comprising the names of licensor providers. 11 | * 12 | * @example 13 | * "CC BY 4.0 LINZ" 14 | * 15 | * @example 16 | * "CC BY 4.0 Nelson City Council, Tasman District Council, Waka Kotahi" 17 | */ 18 | export function createLicensorAttribution(providers?: StacProvider[]): string { 19 | if (providers == null) return `${copyright} LINZ`; 20 | 21 | const licensors = providers.filter((p) => p.roles?.includes('licensor')); 22 | if (licensors.length === 0) return `${copyright} LINZ`; 23 | 24 | return `${copyright} ${licensors.map((l) => l.name).join(', ')}`; 25 | } 26 | -------------------------------------------------------------------------------- /packages/attribution/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "lib": ["DOM"], 5 | "rootDir": "./src", 6 | "outDir": "./build" 7 | }, 8 | "include": ["src"], 9 | "references": [{ "path": "../__tests__/tsconfig.json" }] 10 | } 11 | -------------------------------------------------------------------------------- /packages/attribution/typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "entryPoints": ["./src/**/*.ts"], 3 | "exclude": ["./**/__tests__/*.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/bathymetry/README.md: -------------------------------------------------------------------------------- 1 | # @basemaps/bathymetry 2 | 3 | ## Bathymetry creation 4 | 5 | This process takes batheymetric data from [GEBCO](https://www.gebco.net/) and converts it into a colorized hillshaded geotiff. 6 | 7 | ![](./images/bathyoutput.png) 8 | 9 | ## Usage 10 | 11 | You will need: 12 | 13 | - Gebco netcdf file [here](https://www.gebco.net/data_and_products/gridded_bathymetry_data/) 14 | - Docker (or a new gdal 3+ with netcdf support) 15 | - Node >= v12 16 | 17 | ```bash 18 | # Install dependencies 19 | npm install @basemaps/bathymetry 20 | 21 | # To prevent very long CI/Dev build times, mapnik will need to be manually installed 22 | npm install mapnik 23 | 24 | # Ensure the javascript has been built 25 | npm run build 26 | 27 | # Create a the data file 28 | node build/index.js -v create --input gebco_2020.nc --docker --output gebco/ 29 | ``` 30 | 31 | ## Process 32 | 33 | ![](./images/bathyprocess.png) 34 | -------------------------------------------------------------------------------- /packages/bathymetry/images/bathyoutput.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/packages/bathymetry/images/bathyoutput.png -------------------------------------------------------------------------------- /packages/bathymetry/images/bathyprocess.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/packages/bathymetry/images/bathyprocess.png -------------------------------------------------------------------------------- /packages/bathymetry/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@basemaps/bathymetry", 3 | "version": "8.1.0", 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/linz/basemaps.git", 7 | "directory": "packages/bathymetry" 8 | }, 9 | "author": { 10 | "name": "Land Information New Zealand", 11 | "url": "https://linz.govt.nz", 12 | "organization": true 13 | }, 14 | "type": "module", 15 | "engines": { 16 | "node": ">=16.0.0" 17 | }, 18 | "license": "MIT", 19 | "main": "./build/index.js", 20 | "types": "./build/index.d.ts", 21 | "scripts": { 22 | "test": "node --test" 23 | }, 24 | "publishConfig": { 25 | "access": "public" 26 | }, 27 | "files": [ 28 | "build/" 29 | ], 30 | "dependencies": { 31 | "@basemaps/geo": "^8.0.0", 32 | "@basemaps/shared": "^8.1.0", 33 | "@rushstack/ts-command-line": "^4.3.13", 34 | "ulid": "^2.3.0" 35 | }, 36 | "devDependencies": { 37 | "@types/mapnik": "^3.0.1" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/bathymetry/src/@types/muiltihash.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'multihashes' { 2 | interface MultiHashStatic { 3 | encode(buf: Buffer, type: 'sha2-256'): Buffer; 4 | } 5 | const multiHash: MultiHashStatic; 6 | export = multiHash; 7 | } 8 | -------------------------------------------------------------------------------- /packages/bathymetry/src/__tests__/hash.test.ts: -------------------------------------------------------------------------------- 1 | import assert from 'node:assert'; 2 | import * as fs from 'node:fs'; 3 | import { describe, it } from 'node:test'; 4 | 5 | import { Hash } from '../hash.js'; 6 | 7 | describe('hash', () => { 8 | it('hash', async () => { 9 | fs.writeFileSync('./test-file', Buffer.from('I am a test file for hashing\n')); 10 | const ans = await Hash.hash('./test-file'); 11 | assert.equal(ans, '122076427149ca45100f317f16821ef934885cc49352447ee64c9f5e9655c95c695e'); 12 | fs.unlinkSync('./test-file'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /packages/bathymetry/src/__tests__/test-file.txt: -------------------------------------------------------------------------------- 1 | I am a test file for hashing 2 | -------------------------------------------------------------------------------- /packages/bathymetry/src/folder.ts: -------------------------------------------------------------------------------- 1 | import { Env } from '@basemaps/shared'; 2 | import { promises as fs } from 'fs'; 3 | import * as path from 'path'; 4 | 5 | /** Make a temp folder inside TEMP_FOLDER's path */ 6 | export async function makeTempFolder(folder: string): Promise { 7 | const tempPath = Env.get(Env.TempFolder) ?? '/tmp'; 8 | const folderPath = path.join(tempPath, folder); 9 | 10 | await fs.mkdir(folderPath, { recursive: true }); 11 | return folderPath; 12 | } 13 | 14 | /** Make a tiff folder inside TEMP_FOLDER's path */ 15 | export async function makeTiffFolder(tmpFolder: string, name: string): Promise { 16 | const folderPath = path.join(tmpFolder, name); 17 | 18 | await fs.mkdir(folderPath, { recursive: true }); 19 | return folderPath; 20 | } 21 | -------------------------------------------------------------------------------- /packages/bathymetry/src/gdal/__tests__/gdal.progress.test.ts: -------------------------------------------------------------------------------- 1 | import assert from 'node:assert'; 2 | import { describe, it } from 'node:test'; 3 | 4 | import { GdalProgressParser } from '../gdal.progress.js'; 5 | 6 | describe('GdalProgressParser', () => { 7 | it('should emit on progress', () => { 8 | const prog = new GdalProgressParser(); 9 | assert.equal(prog.progress, 0); 10 | 11 | prog.data(Buffer.from('\n.')); 12 | assert.equal(prog.progress.toFixed(2), '3.23'); 13 | }); 14 | 15 | it('should take 31 dots to finish', () => { 16 | const prog = new GdalProgressParser(); 17 | let processCount = 0; 18 | prog.data(Buffer.from('\n')); 19 | prog.on('progress', () => processCount++); 20 | 21 | for (let i = 0; i < 31; i++) { 22 | prog.data(Buffer.from('.')); 23 | assert.equal(processCount, i + 1); 24 | } 25 | assert.equal(prog.progress.toFixed(2), '100.00'); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /packages/bathymetry/src/gdal/gdal.local.ts: -------------------------------------------------------------------------------- 1 | import { LogType } from '@basemaps/shared'; 2 | 3 | import { GdalCommand } from './gdal.command.js'; 4 | 5 | export class GdalLocal extends GdalCommand { 6 | override async env(): Promise> { 7 | if (this.credentials == null) { 8 | return process.env; 9 | } 10 | if (this.credentials.needsRefresh()) { 11 | await this.credentials.refreshPromise(); 12 | } 13 | return { 14 | ...process.env, 15 | AWS_ACCESS_KEY_ID: this.credentials.accessKeyId, 16 | AWS_SECRET_ACCESS_KEY: this.credentials.secretAccessKey, 17 | AWS_SESSION_TOKEN: this.credentials.sessionToken, 18 | }; 19 | } 20 | 21 | override async run(cmd: string, args: string[], log: LogType): Promise<{ stdout: string; stderr: string }> { 22 | log.debug({ cmd, gdalArgs: args.slice(0, 50).join(' ') }, 'StartGdal:Local'); 23 | return super.run(cmd, args, log); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/bathymetry/src/gdal/gdal.ts: -------------------------------------------------------------------------------- 1 | import { Env, LogType } from '@basemaps/shared'; 2 | 3 | import { GdalCommand } from './gdal.command.js'; 4 | import { GdalDocker } from './gdal.docker.js'; 5 | import { GdalLocal } from './gdal.local.js'; 6 | 7 | export class Gdal { 8 | /** 9 | * Create a new GdalCommand instance ready to run commands 10 | * 11 | * This could be a local or docker container depending on environment variables 12 | * @see Env.Gdal.UseDocker 13 | */ 14 | static create(): GdalCommand { 15 | if (Env.get(Env.Gdal.UseDocker)) return new GdalDocker(); 16 | return new GdalLocal(); 17 | } 18 | 19 | /** 20 | * Run a `gdal_translate --version` to extract the current gdal version 21 | * 22 | * @example "GDAL 2.4.2, released 2019/06/28" 23 | * @example "GDAL 3.2.0dev-69b0c4ec4174fde36c609a4aac6f4281424021b3, released 2020/06/26" 24 | */ 25 | static async version(logger: LogType): Promise { 26 | const gdal = Gdal.create(); 27 | const { stdout } = await gdal.run('gdal_translate', ['--version'], logger); 28 | return stdout; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/bathymetry/src/hash.ts: -------------------------------------------------------------------------------- 1 | import * as crypto from 'crypto'; 2 | import * as fs from 'fs'; 3 | 4 | /** Stream a file though */ 5 | function hashFile(filePath: string, hashName: string): Promise { 6 | return new Promise((resolve, reject) => { 7 | const hash = crypto.createHash(hashName); 8 | const stream = fs.createReadStream(filePath); 9 | stream.on('error', (err) => reject(err)); 10 | stream.on('data', (chunk) => hash.update(chunk)); 11 | stream.on('end', () => resolve(hash.digest('hex'))); 12 | }); 13 | } 14 | 15 | /** Create a multihash of a file */ 16 | async function hash(filePath: string): Promise { 17 | const hashData = await hashFile(filePath, 'sha256'); 18 | // 0x12 - sha256 19 | // 0x20 - 32bytes 20 | return '1220' + hashData; 21 | } 22 | 23 | export const Hash = { hash }; 24 | -------------------------------------------------------------------------------- /packages/bathymetry/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | 4 | "compilerOptions": { 5 | "outDir": "./build", 6 | "rootDir": "./src" 7 | }, 8 | "include": ["src"], 9 | "references": [ 10 | { "path": "../__tests__/tsconfig.json" }, 11 | { "path": "../shared/tsconfig.json" }, 12 | { "path": "../cli/tsconfig.json" } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /packages/cli-config/src/__tests__/util.test.ts: -------------------------------------------------------------------------------- 1 | import assert from 'node:assert'; 2 | import { describe, it } from 'node:test'; 3 | 4 | import { nameImageryTitle } from '../util.js'; 5 | 6 | describe('util', () => { 7 | it('nameImageryTitle', () => { 8 | assert.equal(nameImageryTitle('Palmerston-north urban 2016-17 12.125m'), 'palmerston-north_urban_2016-17_12-125m'); 9 | assert.equal(nameImageryTitle('Palmerston-north urban 2016-17 12-125'), 'palmerston-north_urban_2016-17_12-125'); 10 | assert.equal(nameImageryTitle('Palmerston-north / urban 2016-17 12.125'), 'palmerston-north_urban_2016-17_12-125'); 11 | assert.equal(nameImageryTitle('Palmerston.north / urban 2016-17 12.125'), 'palmerston-north_urban_2016-17_12-125'); 12 | assert.equal(nameImageryTitle('Manawatū urban 2016-17 12.125m'), 'manawatu_urban_2016-17_12-125m'); 13 | assert.equal(nameImageryTitle('ĀāĒēĪīŌōŪū urban 2016-17 12.125m'), 'aaeeiioouu_urban_2016-17_12-125m'); 14 | assert.equal( 15 | nameImageryTitle('Marlborough / Wellington 0.75m SNC50451 (2004-2005)'), 16 | 'marlborough_wellington_0-75m_snc50451_2004-2005', 17 | ); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /packages/cli-config/src/bin.ts: -------------------------------------------------------------------------------- 1 | Error.stackTraceLimit = 100; 2 | import { LogConfig } from '@basemaps/shared'; 3 | import { run } from 'cmd-ts'; 4 | 5 | import { ConfigCli } from './index.js'; 6 | 7 | run(ConfigCli, process.argv.slice(2)).catch((err) => { 8 | const logger = LogConfig.get(); 9 | logger.fatal({ err }, 'Command:Failed'); 10 | 11 | // Give the logger some time to flush before exiting 12 | setTimeout(() => process.exit(1), 25); 13 | }); 14 | -------------------------------------------------------------------------------- /packages/cli-config/src/index.ts: -------------------------------------------------------------------------------- 1 | import { subcommands } from 'cmd-ts'; 2 | 3 | import { BundleAssetsCommand } from './cli/action.bundle.assets.js'; 4 | import { BundleCommand } from './cli/action.bundle.js'; 5 | import { CreateConfigCommand } from './cli/action.create.config.js'; 6 | import { ImportCommand } from './cli/action.import.js'; 7 | 8 | export const ConfigCli = subcommands({ 9 | name: 'config', 10 | cmds: { 11 | bundle: BundleCommand, 12 | 'bundle-assets': BundleAssetsCommand, 13 | import: ImportCommand, 14 | 'create-config': CreateConfigCommand, 15 | }, 16 | }); 17 | -------------------------------------------------------------------------------- /packages/cli-config/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | 4 | "compilerOptions": { 5 | "outDir": "./build", 6 | "rootDir": "./src" 7 | }, 8 | "include": ["src"], 9 | "references": [ 10 | { "path": "../config/tsconfig.json" }, 11 | { "path": "../config-loader/tsconfig.json" }, 12 | { "path": "../geo/tsconfig.json" }, 13 | { "path": "../linzjs-metrics/tsconfig.json" }, 14 | { "path": "../shared/tsconfig.json" } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /packages/cli-config/typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "entryPoints": ["./src/**/*.ts"], 3 | "exclude": ["./**/__tests__/*.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/cli-raster/src/bin.ts: -------------------------------------------------------------------------------- 1 | Error.stackTraceLimit = 100; 2 | 3 | import { Fqdn, fsa, LogConfig } from '@basemaps/shared'; 4 | import { run } from 'cmd-ts'; 5 | 6 | import { CogifyCli } from './cogify/cli.js'; 7 | 8 | // Force Fully qualified domain names to reduce DNS lookups 9 | Fqdn.isForcedFqdn = true; 10 | 11 | // remove the source caching / chunking as it is not needed for cogify, cogify only reads tiffs once so caching the result is not helpful 12 | fsa.middleware = fsa.middleware.filter((f) => f.name !== 'source:chunk'); 13 | fsa.middleware = fsa.middleware.filter((f) => f.name !== 'source:cache'); 14 | 15 | run(CogifyCli, process.argv.slice(2)).catch((err) => { 16 | const logger = LogConfig.get(); 17 | logger.fatal({ err }, 'Command:Failed'); 18 | 19 | // Give the logger some time to flush before exiting 20 | setTimeout(() => process.exit(1), 25); 21 | }); 22 | -------------------------------------------------------------------------------- /packages/cli-raster/src/cogify/cli.ts: -------------------------------------------------------------------------------- 1 | import { subcommands } from 'cmd-ts'; 2 | 3 | import { BasemapsCogifyCreateCommand } from './cli/cli.cog.js'; 4 | import { BasemapsCogifyCoverCommand } from './cli/cli.cover.js'; 5 | import { TopoStacCreationCommand } from './cli/cli.topo.js'; 6 | import { BasemapsCogifyValidateCommand } from './cli/cli.validate.js'; 7 | 8 | export const CogifyCli = subcommands({ 9 | name: 'cogify', 10 | cmds: { 11 | cover: BasemapsCogifyCoverCommand, 12 | create: BasemapsCogifyCreateCommand, 13 | validate: BasemapsCogifyValidateCommand, 14 | topo: TopoStacCreationCommand, 15 | }, 16 | }); 17 | -------------------------------------------------------------------------------- /packages/cli-raster/src/cogify/cli/cli.validate.ts: -------------------------------------------------------------------------------- 1 | import { getLogger, logArguments, Url } from '@basemaps/shared'; 2 | import { CliInfo } from '@basemaps/shared/build/cli/info.js'; 3 | import { command, number, option, restPositionals } from 'cmd-ts'; 4 | import pLimit from 'p-limit'; 5 | 6 | import { validateOutputTiff } from './cli.cog.js'; 7 | 8 | export const BasemapsCogifyValidateCommand = command({ 9 | name: 'cogify-validate', 10 | version: CliInfo.version, 11 | description: 'Validate a COG is created in a way basemaps likes', 12 | args: { 13 | ...logArguments, 14 | concurrency: option({ 15 | type: number, 16 | long: 'concurrency', 17 | description: 'How many COGs to initialise at once', 18 | defaultValue: () => 25, 19 | defaultValueIsSerializable: true, 20 | }), 21 | tiffs: restPositionals({ type: Url, displayName: 'paths', description: 'COG to validate' }), 22 | }, 23 | 24 | async handler(args) { 25 | const logger = getLogger(this, args, 'cli-raster'); 26 | const q = pLimit(args.concurrency); 27 | 28 | const promises = args.tiffs.map((tiff) => { 29 | return q(() => validateOutputTiff(tiff, undefined, logger)); 30 | }); 31 | 32 | await Promise.allSettled(promises); 33 | }, 34 | }); 35 | -------------------------------------------------------------------------------- /packages/cli-raster/src/cogify/topo/slug.ts: -------------------------------------------------------------------------------- 1 | import { EpsgCode } from '@basemaps/geo'; 2 | import { LogType } from '@basemaps/shared'; 3 | 4 | const Slugs: Partial> = { 5 | [EpsgCode.Nztm2000]: 'new-zealand-mainland', 6 | [EpsgCode.Citm2000]: 'chatham-islands', 7 | }; 8 | 9 | /** 10 | * Attempts to map the given EpsgCode enum to a slug. 11 | * 12 | * @param epsg: The EpsgCode enum to map to a slug 13 | * 14 | * @returns if succeeded, a slug string. Otherwise, null. 15 | */ 16 | export function mapEpsgToSlug(epsg: EpsgCode, logger?: LogType): string | null { 17 | const slug = Slugs[epsg]; 18 | 19 | logger?.info({ found: slug != null }, 'mapEpsgToSlug()'); 20 | return slug ?? null; 21 | } 22 | -------------------------------------------------------------------------------- /packages/cli-raster/src/index.ts: -------------------------------------------------------------------------------- 1 | export { CogifyCli } from './cogify/cli.js'; 2 | -------------------------------------------------------------------------------- /packages/cli-raster/static/example-covering.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/packages/cli-raster/static/example-covering.png -------------------------------------------------------------------------------- /packages/cli-raster/static/nztm-tile-index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/packages/cli-raster/static/nztm-tile-index.png -------------------------------------------------------------------------------- /packages/cli-raster/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | 4 | "compilerOptions": { 5 | "outDir": "./build", 6 | "rootDir": "./src" 7 | }, 8 | "include": ["src"], 9 | "references": [ 10 | { "path": "../config/tsconfig.json" }, 11 | { "path": "../geo/tsconfig.json" }, 12 | { "path": "../server/tsconfig.json" }, 13 | { "path": "../shared/tsconfig.json" }, 14 | { "path": "../lambda-tiler/tsconfig.json" }, 15 | { "path": "../tiler-sharp/tsconfig.json" } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /packages/cli-raster/typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "entryPoints": ["./src/**/*.ts"], 3 | "exclude": ["./**/__test__/*.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/cli-vector/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # [8.1.0](https://github.com/linz/basemaps/compare/v8.0.0...v8.1.0) (2025-05-18) 7 | 8 | **Note:** Version bump only for package @basemaps/cli-vector 9 | 10 | 11 | 12 | 13 | 14 | # [8.0.0](https://github.com/linz/basemaps/compare/v7.17.0...v8.0.0) (2025-05-11) 15 | 16 | 17 | ### Features 18 | 19 | * **cli-vector:** Extract cli to load schema json and prepare jobs to process vector mbtiles. BM-1267 ([#3429](https://github.com/linz/basemaps/issues/3429)) ([db113e2](https://github.com/linz/basemaps/commit/db113e27ad935fab4538ffad607c2cd04f52dbdd)) 20 | * **cli:** move cogify create-config into cli-config package BM-1261 ([#3432](https://github.com/linz/basemaps/issues/3432)) ([5f72430](https://github.com/linz/basemaps/commit/5f72430690d330e8542d272ede461d3a711493de)) 21 | 22 | 23 | 24 | 25 | 26 | # Change Log 27 | -------------------------------------------------------------------------------- /packages/cli-vector/README.md: -------------------------------------------------------------------------------- 1 | # @basemaps/cli-vector 2 | 3 | CLI to create vector mbtiles for topographic map. 4 | 5 | ## Usage -- Bundle 6 | 7 | Extract and load schema.json config files then prepare tasks for the next step to create mbtiles 8 | 9 | ```bash 10 | node build/bin.js extract --path schema/ --cache s3://linz-basemaps-staging/vector/cache/ 11 | ``` 12 | -------------------------------------------------------------------------------- /packages/cli-vector/schema/addresses.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "addresses", 3 | "metadata": { "attributes": ["housenumber", "name", "id"] }, 4 | "layers": [ 5 | { 6 | "id": "105689", 7 | "name": "105689-nz-addresses-pilot", 8 | "source": "s3://linz-lds-cache/105689/", 9 | "tags": {}, 10 | "attributes": { "address_id": "id", "full_address_number": "housenumber" }, 11 | "style": { "minZoom": 15, "maxZoom": 15 } 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /packages/cli-vector/schema/buildings.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "buildings", 3 | "metadata": { "attributes": ["building", "store_item", "kind", "name", "use"] }, 4 | "layers": [ 5 | { 6 | "id": "101290", 7 | "name": "101290-nz-building-outlines", 8 | "source": "s3://linz-lds-cache/101290/", 9 | "tags": { "kind": "building" }, 10 | "style": { "minZoom": 14, "maxZoom": 15 }, 11 | "tippecanoe": ["--detect-shared-borders"] 12 | }, 13 | { 14 | "id": "50361", 15 | "name": "50361-nz-tank-polygons-topo-150k", 16 | "source": "s3://linz-lds-cache/50361/", 17 | "tags": { "building": "storage_tank" }, 18 | "style": { "minZoom": 12, "maxZoom": 15 } 19 | }, 20 | { 21 | "id": "52280", 22 | "name": "52280-cook-islands-tank-polygons-topo-125k-zone4", 23 | "source": "s3://linz-lds-cache/52280/", 24 | "tags": { "building": "storage_tank" }, 25 | "style": { "minZoom": 0, "maxZoom": 15 } 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /packages/cli-vector/schema/dam_lines.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dam_lines", 3 | "metadata": { "attributes": ["kind", "name", "dam_status"] }, 4 | "layers": [ 5 | { 6 | "id": "50075", 7 | "name": "50075-nz-chatham-island-dam-centrelines-topo-150k", 8 | "source": "s3://linz-lds-cache/50075/", 9 | "tags": { "kind": "dam" }, 10 | "style": { "minZoom": 0, "maxZoom": 15 } 11 | }, 12 | { 13 | "id": "50260", 14 | "name": "50260-nz-dam-centrelines-topo-150k", 15 | "source": "s3://linz-lds-cache/50260/", 16 | "tags": { "kind": "dam" }, 17 | "style": { "minZoom": 12, "maxZoom": 15 } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /packages/cli-vector/schema/ferries.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ferries", 3 | "metadata": { "attributes": ["kind", "name"] }, 4 | "layers": [ 5 | { 6 | "id": "50269", 7 | "name": "50269-nz-ferry-crossing-centrelines-topo-150k", 8 | "source": "s3://linz-lds-cache/50269/", 9 | "tags": { "kind": "ferry" }, 10 | "style": { "minZoom": 10, "maxZoom": 15 } 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /packages/cli-vector/schema/parcel_boundaries.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "parcel_boundaries", 3 | "metadata": { "attributes": ["parcel_intent", "name", "id"] }, 4 | "layers": [ 5 | { 6 | "id": "50772", 7 | "name": "50772-nz-primary-parcels", 8 | "source": "s3://linz-lds-cache/50772/", 9 | "tags": { "kind": "parcel" }, 10 | "style": { "minZoom": 14, "maxZoom": 15, "detail": 14 }, 11 | "tippecanoe": ["--detect-shared-borders"] 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /packages/cli-vector/schema/pier_lines.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pier_lines", 3 | "metadata": { "attributes": ["kind", "name"] }, 4 | "layers": [ 5 | { 6 | "id": "50243", 7 | "name": "50243-nz-breakwater-centrelines-topo-150k", 8 | "source": "s3://linz-lds-cache/50243/", 9 | "tags": { "kind": "breakwater" }, 10 | "style": { "minZoom": 12, "maxZoom": 15 } 11 | }, 12 | { 13 | "id": "50066", 14 | "name": "50066-nz-chatham-island-breakwater-centrelines-topo-150k", 15 | "source": "s3://linz-lds-cache/50066/", 16 | "tags": { "kind": "breakwater" }, 17 | "style": { "minZoom": 12, "maxZoom": 15 } 18 | }, 19 | { 20 | "id": "52141", 21 | "name": "52141-tokelau-breakwater-centrelines-topo-125k", 22 | "source": "s3://linz-lds-cache/52141/", 23 | "tags": { "kind": "breakwater" }, 24 | "style": { "minZoom": 12, "maxZoom": 15 } 25 | }, 26 | { 27 | "id": "52234", 28 | "name": "52234-cook-islands-breakwater-centrelines-topo-125k-zone4", 29 | "source": "s3://linz-lds-cache/52234/", 30 | "tags": { "kind": "breakwater" }, 31 | "style": { "minZoom": 12, "maxZoom": 15 } 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /packages/cli-vector/schema/place_labels.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "place_labels", 3 | "metadata": { "attributes": ["water", "name", "natural", "place"] }, 4 | "layers": [ 5 | { 6 | "id": "51154", 7 | "name": "51154-nzgb-gazetteer-application-labels-wfs-layer", 8 | "source": "s3://linz-lds-cache/51154/", 9 | "tags": { "kind": "place" }, 10 | "style": { "minZoom": 0, "maxZoom": 15 } 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /packages/cli-vector/schema/street_labels.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "street_labels", 3 | "metadata": { "attributes": ["kind", "ref", "name"] }, 4 | "layers": [ 5 | { 6 | "id": "50329", 7 | "name": "50329-nz-road-centrelines-topo-150k", 8 | "source": "s3://linz-lds-cache/50329/", 9 | "tags": { "kind": "road" }, 10 | "style": { "minZoom": 8, "maxZoom": 15 }, 11 | "simplify": [ 12 | { "style": { "minZoom": 0, "maxZoom": 0 }, "tolerance": 0.1 }, 13 | { "style": { "minZoom": 1, "maxZoom": 1 }, "tolerance": 0.01 }, 14 | { "style": { "minZoom": 2, "maxZoom": 2 }, "tolerance": 0.01 }, 15 | { "style": { "minZoom": 3, "maxZoom": 3 }, "tolerance": 0.03 }, 16 | { "style": { "minZoom": 4, "maxZoom": 4 }, "tolerance": 0.008 }, 17 | { "style": { "minZoom": 5, "maxZoom": 5 }, "tolerance": 0.004 }, 18 | { "style": { "minZoom": 6, "maxZoom": 6 }, "tolerance": 0.001 }, 19 | { "style": { "minZoom": 7, "maxZoom": 7 }, "tolerance": 0.00075 }, 20 | { "style": { "minZoom": 8, "maxZoom": 8 }, "tolerance": 0.0005 }, 21 | { "style": { "minZoom": 9, "maxZoom": 9 }, "tolerance": 0.00025 }, 22 | { "style": { "minZoom": 10, "maxZoom": 10 }, "tolerance": 0.0001 }, 23 | { "style": { "minZoom": 11, "maxZoom": 15 } } 24 | ] 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /packages/cli-vector/src/bin.ts: -------------------------------------------------------------------------------- 1 | Error.stackTraceLimit = 100; 2 | import { LogConfig } from '@basemaps/shared'; 3 | import { run } from 'cmd-ts'; 4 | 5 | import { VectorCli } from './index.js'; 6 | 7 | run(VectorCli, process.argv.slice(2)).catch((err) => { 8 | const logger = LogConfig.get(); 9 | logger.fatal({ err }, 'Command:Failed'); 10 | 11 | // Give the logger some time to flush before exiting 12 | setTimeout(() => process.exit(1), 25); 13 | }); 14 | -------------------------------------------------------------------------------- /packages/cli-vector/src/index.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line simple-import-sort/imports 2 | import { subcommands } from 'cmd-ts'; 3 | import { ExtractCommand } from './cli/cli.extract.js'; 4 | import { CreateCommand } from './cli/cli.create.js'; 5 | import { JoinCommand } from './cli/cli.join.js'; 6 | 7 | export const VectorCli = subcommands({ 8 | name: 'vector', 9 | cmds: { 10 | extract: ExtractCommand, 11 | create: CreateCommand, 12 | join: JoinCommand, 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /packages/cli-vector/src/modify/consts.ts: -------------------------------------------------------------------------------- 1 | export const MajorHighWays: Readonly> = new Set([ 2 | '1', 3 | '1B', 4 | '2', 5 | '3', 6 | '3A', 7 | '4', 8 | '5', 9 | '6', 10 | '6A', 11 | '7', 12 | '8', 13 | '8A', 14 | '18', 15 | '20', 16 | '51', 17 | '76', 18 | '73', 19 | '1,3', 20 | '6,94', 21 | '6,96', 22 | ]); 23 | -------------------------------------------------------------------------------- /packages/cli-vector/src/modify/layers/pois.ts: -------------------------------------------------------------------------------- 1 | import { LogType } from '@basemaps/shared'; 2 | 3 | import { VectorGeoFeature } from '../../types/VectorGeoFeature.js'; 4 | 5 | /** 6 | * Processes a 'pois' layer feature. 7 | * 8 | * @param feature - the feature to process 9 | * @param options - the layer's options 10 | * @param logger - a logger instance 11 | * @returns the processed feature 12 | */ 13 | export function handleLayerPois(feature: VectorGeoFeature, logger: LogType): VectorGeoFeature | null { 14 | logger.trace({}, 'HandlePois:Start'); 15 | feature = structuredClone(feature); 16 | 17 | // REVIEW: We don't have any use for this criteria as we don't include the following layers: 18 | // 1. 50245-nz-building-points-topo-150k 19 | // 2. 50246-nz-building-polygons-topo-150k 20 | if (feature.properties['building'] === 'building') { 21 | const bldgUse = feature.properties['bldg_use']; 22 | 23 | if (bldgUse == null) { 24 | // discard the feature 25 | logger.trace({}, 'HandlePois:End'); 26 | return null; 27 | } 28 | 29 | feature.properties['building'] = bldgUse; 30 | } 31 | 32 | logger.trace({}, 'HandlePois:End'); 33 | return feature; 34 | } 35 | -------------------------------------------------------------------------------- /packages/cli-vector/src/modify/parser.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const zPlaceLabelsProperties = z.object({ 4 | /** @example "Kaitaia" */ 5 | label: z.string(), 6 | 7 | /** @example "TWN1" */ 8 | style: z.string(), 9 | 10 | /** @example "city" */ 11 | place: z.string(), 12 | 13 | /** @example 7 */ 14 | adminlevel: z.number(), 15 | 16 | /** @example "0" */ 17 | natural: z.string(), 18 | 19 | /** @example "0" */ 20 | water: z.string(), 21 | }); 22 | 23 | export const zPlaceLabelsTippecanoe = z.object({ 24 | /** @example "place_labels" */ 25 | layer: z.string(), 26 | 27 | /** @example 8 */ 28 | minzoom: z.number(), 29 | 30 | /** @example 8 */ 31 | maxzoom: z.number(), 32 | }); 33 | 34 | export type zTypePlaceLabelsProperties = z.infer; 35 | export type zTypePlaceLabelsTippecanoe = z.infer; 36 | -------------------------------------------------------------------------------- /packages/cli-vector/src/modify/schema.ts: -------------------------------------------------------------------------------- 1 | import { VectorGeoFeature } from '../types/VectorGeoFeature.js'; 2 | 3 | export interface VectorGeoPlaceLabelsFeature extends VectorGeoFeature { 4 | properties: { 5 | /** @example "Kaitaia" */ 6 | name: string; 7 | 8 | /** @example "ant" */ 9 | kind: string; 10 | 11 | /** @example "city" */ 12 | place: string; 13 | 14 | /** @example 7 */ 15 | adminlevel: number; 16 | 17 | /** @example "0" */ 18 | natural: string; 19 | 20 | /** @example "0" */ 21 | water: string; 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /packages/cli-vector/src/schema-loader/parser.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const zStyling = z.object({ 4 | minZoom: z.number(), 5 | maxZoom: z.number(), 6 | detail: z.number().optional(), 7 | }); 8 | 9 | export const zTags = z.record(z.string().or(z.boolean())); 10 | 11 | export const zAttributes = z.record(z.string()); 12 | 13 | export const zSpecialTag = z.object({ 14 | condition: z.string(), 15 | tags: zTags, 16 | }); 17 | 18 | export const zSimplify = z.object({ 19 | style: zStyling, 20 | tolerance: z.number().optional(), 21 | }); 22 | 23 | export const zLayer = z.object({ 24 | id: z.string(), 25 | name: z.string(), 26 | version: z.number().optional(), 27 | source: z.string(), 28 | tags: zTags, 29 | attributes: zAttributes.optional(), 30 | style: zStyling, 31 | simplify: z.array(zSimplify).optional(), 32 | tippecanoe: z.array(z.string()).optional(), 33 | }); 34 | 35 | export const zSchema = z.object({ 36 | name: z.string(), 37 | metadata: z.object({ 38 | attributes: z.array(z.string()), 39 | }), 40 | simplify: z.array(zSimplify).optional(), 41 | layers: z.array(zLayer), 42 | }); 43 | 44 | export type zTypeLayer = z.infer; 45 | export type zTypeSchema = z.infer; 46 | -------------------------------------------------------------------------------- /packages/cli-vector/src/transform/covt.ts: -------------------------------------------------------------------------------- 1 | import { fsa, LogType, SourceMemory } from '@basemaps/shared'; 2 | import { CotarIndexBuilder, CotarIndexOptions, TarReader } from '@cotar/builder'; 3 | import { CotarIndex } from '@cotar/core'; 4 | import { promises as fs } from 'fs'; 5 | 6 | /** 7 | * Create index for the COVT tar file 8 | */ 9 | export async function toTarIndex(input: string, output: string, logger: LogType): Promise { 10 | logger.info({ output: output }, 'Cotar.Index:Start'); 11 | 12 | const fd = await fs.open(input, 'r'); 13 | 14 | const opts: CotarIndexOptions = { packingFactor: 1.25, maxSearch: 50 }; // Default package rule. 15 | const { buffer, count } = await CotarIndexBuilder.create(fd, opts); 16 | 17 | const index = await CotarIndex.create(new SourceMemory('index', buffer)); 18 | await TarReader.validate(fd, index); 19 | await fs.writeFile(output, buffer); 20 | await fs.appendFile(input, buffer); 21 | 22 | await fd.close(); 23 | if (!(await fsa.exists(fsa.toUrl(output)))) throw new Error('Error - Cotar.Index creation Failure.'); 24 | 25 | logger.info({ index, count }, 'Cotar.Index:Done'); 26 | return output; 27 | } 28 | -------------------------------------------------------------------------------- /packages/cli-vector/src/transform/ogr2ogr.ts: -------------------------------------------------------------------------------- 1 | import { Epsg } from '@basemaps/geo'; 2 | import { LogType } from '@basemaps/shared'; 3 | import { Command } from '@linzjs/docker-command'; 4 | 5 | /** 6 | * ogr2ogr GeoJSONSeq usage, return cmd for ogr2ogr 7 | * 8 | * @returns {cmd: string, args: string[]} cmd and arguments for ogr2ogr 9 | */ 10 | export async function ogr2ogrNDJson(input: URL, output: URL, logger: LogType): Promise { 11 | const cmd = Command.create('ogr2ogr'); 12 | 13 | cmd.args.push('-f', 'GeoJSONSeq'); 14 | cmd.args.push(output.pathname); 15 | 16 | cmd.args.push('-t_srs', Epsg.Wgs84.toEpsgString()); 17 | cmd.args.push(input.pathname); 18 | 19 | const res = await cmd.run(); 20 | if (res.exitCode !== 0) { 21 | logger.fatal({ Gdal: res }, 'Failure'); 22 | throw new Error('Gdal failed to run'); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/cli-vector/src/types/VectorGeoFeature.ts: -------------------------------------------------------------------------------- 1 | import { Feature } from 'geojson'; 2 | 3 | export interface VectorGeoFeature extends Feature { 4 | properties: Record; 5 | tippecanoe: { 6 | layer: string; 7 | minzoom: number; 8 | maxzoom: number; 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /packages/cli-vector/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | 4 | "compilerOptions": { 5 | "outDir": "./build", 6 | "rootDir": "./src" 7 | }, 8 | "include": ["src"], 9 | "references": [ 10 | { "path": "../shared/tsconfig.json" }, 11 | { "path": "../geo/tsconfig.json" }, 12 | { "path": "../config/tsconfig.json" } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /packages/cli-vector/typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "entryPoints": ["./src/**/*.ts"], 3 | "exclude": ["./**/__test__/*.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/cli/README.md: -------------------------------------------------------------------------------- 1 | # @basemaps/cli 2 | 3 | This package contains cli's that are mainly used by the configuration and CICD processes in the LINZ Basemaps. All of the cli commands in this package will be build into docker container and publish as a [github container](https://github.com/linz/basemaps/pkgs/container/basemaps%2Fcli) 4 | 5 | ## Install 6 | 7 | This script requires docker to be installed 8 | 9 | To install 10 | 11 | ```bash 12 | npm i @basemaps/cli 13 | ``` 14 | 15 | ## Usage -- Config 16 | 17 | Config clis in @basemap/cli-config 18 | 19 | ```bash 20 | ./bin/bmc.js config --help 21 | ``` -------------------------------------------------------------------------------- /packages/cli/__test.assets__/kapiti.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "name": "kapiti2", 4 | "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, 5 | "features": [ 6 | { "type": "Feature", "properties": { "name": "Kapiti Island", "macronated": "N", "grp_macron": "N", "TARGET_FID": "4827", "grp_ascii": null, "grp_name": null, "name_ascii": "Kapiti Island" }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 174.939296001000116, -40.820378950066505 ], [ 174.951823768000025, -40.826017232066491 ], [ 174.943638325000052, -40.832650322066435 ], [ 174.94016643300003, -40.840112962066449 ], [ 174.933772999000126, -40.849095342066356 ], [ 174.919346271000109, -40.866022574066292 ], [ 174.909681521000039, -40.871987498066268 ], [ 174.903924421999989, -40.87847411406625 ], [ 174.897163602000063, -40.883725928066241 ], [ 174.889816430000138, -40.887456639066237 ], [ 174.882710580000094, -40.889738312066214 ], [ 174.876440507000069, -40.8898498640662 ], [ 174.86980983700002, -40.882956531066256 ], [ 174.879634448000047, -40.870214745066271 ], [ 174.891214816000087, -40.858532232066331 ], [ 174.910775275000105, -40.844398138066403 ], [ 174.92093585300006, -40.827471407066497 ], [ 174.922485574000063, -40.822283591066508 ], [ 174.932190368000079, -40.822727489066523 ], [ 174.936550736000044, -40.820684332066527 ], [ 174.936722733000096, -40.820648581066479 ], [ 174.939296001000116, -40.820378950066505 ] ] ] ] } } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /packages/cli/__test.assets__/tif1.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/packages/cli/__test.assets__/tif1.tiff -------------------------------------------------------------------------------- /packages/cli/__test.assets__/tif2.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/packages/cli/__test.assets__/tif2.tiff -------------------------------------------------------------------------------- /packages/cli/bin/bmc.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import '../build/cli/bin.js'; 4 | -------------------------------------------------------------------------------- /packages/cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@basemaps/cli", 3 | "version": "8.1.0", 4 | "private": false, 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/linz/basemaps.git", 8 | "directory": "packages/cli" 9 | }, 10 | "author": { 11 | "name": "Land Information New Zealand", 12 | "url": "https://linz.govt.nz", 13 | "organization": true 14 | }, 15 | "license": "MIT", 16 | "main": "./build/index.js", 17 | "types": "./build/index.d.ts", 18 | "bin": { 19 | "bmc": "./bmc.js" 20 | }, 21 | "scripts": { 22 | "build": "tsc", 23 | "bundle": "../../scripts/bundle.mjs package.json", 24 | "test": "node --test" 25 | }, 26 | "bundle": [ 27 | { 28 | "entry": "src/cli/bin.ts", 29 | "minify": false, 30 | "outfile": "dist/index.cjs", 31 | "external": [ 32 | "sharp", 33 | "pino-pretty", 34 | "node:sqlite", 35 | "lerc" 36 | ] 37 | } 38 | ], 39 | "type": "module", 40 | "engines": { 41 | "node": ">=16.0.0" 42 | }, 43 | "dependencies": { 44 | "@basemaps/cli-config": "^8.1.0", 45 | "@basemaps/cli-raster": "^8.1.0", 46 | "@basemaps/cli-vector": "^8.1.0", 47 | "@basemaps/shared": "^8.1.0", 48 | "cmd-ts": "^0.13.0" 49 | }, 50 | "publishConfig": { 51 | "access": "public" 52 | }, 53 | "files": [ 54 | "build/" 55 | ] 56 | } 57 | -------------------------------------------------------------------------------- /packages/cli/src/cli/bin.ts: -------------------------------------------------------------------------------- 1 | Error.stackTraceLimit = 100; 2 | import { LogConfig } from '@basemaps/shared'; 3 | import { run } from 'cmd-ts'; 4 | 5 | import { Cli } from './index.js'; 6 | 7 | run(Cli, process.argv.slice(2)).catch((err) => { 8 | const logger = LogConfig.get(); 9 | logger.fatal({ err }, 'Command:Failed'); 10 | 11 | // Give the logger some time to flush before exiting 12 | setTimeout(() => process.exit(1), 25); 13 | }); 14 | -------------------------------------------------------------------------------- /packages/cli/src/cli/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { ConfigCli } from '@basemaps/cli-config'; 3 | import { CogifyCli } from '@basemaps/cli-raster'; 4 | import { VectorCli } from '@basemaps/cli-vector'; 5 | import { subcommands } from 'cmd-ts'; 6 | 7 | export const Cli = subcommands({ 8 | name: 'bmc', 9 | description: 'Basemaps command tools', 10 | cmds: { 11 | config: ConfigCli, 12 | cogify: CogifyCli, 13 | vector: VectorCli, 14 | }, 15 | }); 16 | -------------------------------------------------------------------------------- /packages/cli/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | 4 | "compilerOptions": { 5 | "outDir": "./build", 6 | "rootDir": "./src" 7 | }, 8 | "include": ["src"], 9 | "references": [ 10 | { "path": "../cli-config/tsconfig.json" }, 11 | { "path": "../cli-raster/tsconfig.json" }, 12 | { "path": "../cli-vector/tsconfig.json" }, 13 | { "path": "../shared/tsconfig.json" } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /packages/cli/typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "entryPoints": ["./src/**/*.ts"], 3 | "exclude": ["./**/__tests__/*.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/config-loader/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@basemaps/config-loader", 3 | "version": "8.1.0", 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/linz/basemaps.git", 7 | "directory": "packages/config-loader" 8 | }, 9 | "author": { 10 | "name": "Land Information New Zealand", 11 | "url": "https://linz.govt.nz", 12 | "organization": true 13 | }, 14 | "type": "module", 15 | "engines": { 16 | "node": ">=16.0.0" 17 | }, 18 | "license": "MIT", 19 | "main": "./build/index.js", 20 | "types": "./build/index.d.ts", 21 | "scripts": { 22 | "test": "node --test" 23 | }, 24 | "publishConfig": { 25 | "access": "public" 26 | }, 27 | "files": [ 28 | "build/" 29 | ], 30 | "dependencies": { 31 | "@basemaps/config": "^8.1.0", 32 | "@basemaps/geo": "^8.0.0", 33 | "@basemaps/shared": "^8.1.0" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/config-loader/src/index.ts: -------------------------------------------------------------------------------- 1 | export { ConfigJson, isEmptyTiff } from './json/json.config.js'; 2 | export { ConfigImageryTiff, initConfigFromUrls, initImageryFromTiffUrl } from './json/tiff.config.js'; 3 | -------------------------------------------------------------------------------- /packages/config-loader/src/json/log.ts: -------------------------------------------------------------------------------- 1 | export interface LogFunc { 2 | (msg: string): void; 3 | (obj: Record, msg: string): void; 4 | } 5 | 6 | /** 7 | * Expose log type so functions that do not have direct access to pino have access to the log type 8 | */ 9 | export interface LogType { 10 | level: string; 11 | trace: LogFunc; 12 | debug: LogFunc; 13 | info: LogFunc; 14 | warn: LogFunc; 15 | error: LogFunc; 16 | fatal: LogFunc; 17 | child: (obj: Record) => LogType; 18 | } 19 | -------------------------------------------------------------------------------- /packages/config-loader/src/json/parse.provider.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | const zServiceIdentification = z.object({ 4 | title: z.string(), 5 | description: z.string(), 6 | fees: z.string(), 7 | accessConstraints: z.string(), 8 | }); 9 | 10 | const zAddress = z.object({ 11 | deliveryPoint: z.string(), 12 | city: z.string(), 13 | postalCode: z.string(), 14 | country: z.string(), 15 | email: z.string(), 16 | }); 17 | 18 | const zContact = z.object({ 19 | individualName: z.string(), 20 | position: z.string(), 21 | phone: z.string(), 22 | address: zAddress, 23 | }); 24 | 25 | const zServiceProvider = z.object({ 26 | name: z.string(), 27 | site: z.string(), 28 | contact: zContact, 29 | }); 30 | 31 | export const zProviderConfig = z.object({ 32 | id: z.string(), 33 | serviceIdentification: zServiceIdentification, 34 | serviceProvider: zServiceProvider, 35 | }); 36 | 37 | export type ProviderConfigSchema = z.infer; 38 | -------------------------------------------------------------------------------- /packages/config-loader/src/json/parse.style.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const zStyleJson = z.object({ 4 | id: z.string(), 5 | version: z.number(), 6 | name: z.string(), 7 | metadata: z.unknown().optional(), 8 | sprite: z.string().optional(), 9 | glyphs: z.string().optional(), 10 | sources: z.unknown(), 11 | 12 | // TODO it would be good to actually validate all the styles 13 | layers: z.array(z.unknown()), 14 | sky: z.unknown(), 15 | }); 16 | 17 | export type StyleJsonConfigSchema = z.infer; 18 | -------------------------------------------------------------------------------- /packages/config-loader/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "outDir": "./build", 5 | "rootDir": "./src" 6 | }, 7 | "include": ["src"], 8 | "references": [ 9 | { "path": "../__tests__/tsconfig.json" }, 10 | { "path": "../config/tsconfig.json" }, 11 | { "path": "../shared/tsconfig.json" } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /packages/config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@basemaps/config", 3 | "version": "8.1.0", 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/linz/basemaps.git", 7 | "directory": "packages/config" 8 | }, 9 | "author": { 10 | "name": "Land Information New Zealand", 11 | "url": "https://linz.govt.nz", 12 | "organization": true 13 | }, 14 | "type": "module", 15 | "engines": { 16 | "node": ">=16.0.0" 17 | }, 18 | "license": "MIT", 19 | "main": "./build/index.js", 20 | "types": "./build/index.d.ts", 21 | "scripts": { 22 | "test": "node --test" 23 | }, 24 | "publishConfig": { 25 | "access": "public" 26 | }, 27 | "files": [ 28 | "build/" 29 | ], 30 | "dependencies": { 31 | "@basemaps/geo": "^8.0.0", 32 | "base-x": "^4.0.0", 33 | "zod": "^3.17.3" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/config/src/__tests__/prefix.test.ts: -------------------------------------------------------------------------------- 1 | import assert from 'node:assert'; 2 | import { describe, it } from 'node:test'; 3 | 4 | import { ConfigId } from '../base.config.js'; 5 | import { ConfigPrefix } from '../config/prefix.js'; 6 | 7 | describe('ConfigPrefix', () => { 8 | it('should prefix values', () => { 9 | assert.equal(ConfigId.prefix(ConfigPrefix.TileSet, '123'), 'ts_123'); 10 | assert.equal(ConfigId.prefix(ConfigPrefix.Imagery, '123'), 'im_123'); 11 | }); 12 | 13 | it('should unprefix values', () => { 14 | assert.equal(ConfigId.unprefix(ConfigPrefix.TileSet, 'ts_123'), '123'); 15 | assert.equal(ConfigId.unprefix(ConfigPrefix.Imagery, 'im_123'), '123'); 16 | }); 17 | 18 | it('should not unprefix unknown values', () => { 19 | assert.equal(ConfigId.unprefix(ConfigPrefix.TileSet, 'im_123'), 'im_123'); 20 | assert.equal(ConfigId.unprefix(ConfigPrefix.Imagery, 'ts_123'), 'ts_123'); 21 | }); 22 | 23 | it('should get prefix values', () => { 24 | assert.equal(ConfigId.getPrefix('ts_123'), ConfigPrefix.TileSet); 25 | assert.equal(ConfigId.getPrefix('im_123'), ConfigPrefix.Imagery); 26 | }); 27 | 28 | it('should not return unknown prefixes', () => { 29 | assert.equal(ConfigId.getPrefix('jj_123'), null); 30 | assert.equal(ConfigId.getPrefix('123'), null); 31 | assert.equal(ConfigId.getPrefix('_123'), null); 32 | assert.equal(ConfigId.getPrefix('123_123'), null); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /packages/config/src/base58.node.ts: -------------------------------------------------------------------------------- 1 | import { BinaryLike, createHash } from 'crypto'; 2 | 3 | import { base58, isBase58 } from './base58.js'; 4 | 5 | /** Hash something with sha256 then encode it as a base58 text string */ 6 | export function sha256base58(obj: BinaryLike): string { 7 | return base58.encode(createHash('sha256').update(obj).digest()); 8 | } 9 | 10 | export function ensureBase58(s: null): null; 11 | export function ensureBase58(s: string): string; 12 | export function ensureBase58(s: string | null): string | null; 13 | export function ensureBase58(s: string | null): string | null { 14 | if (s == null) return null; 15 | if (isBase58(s)) return s; 16 | return base58.encode(Buffer.from(s)); 17 | } 18 | -------------------------------------------------------------------------------- /packages/config/src/base58.ts: -------------------------------------------------------------------------------- 1 | /** This file is able to be directly imported in the web, soo all nodejs logic is in ./base58.node.ts */ 2 | import baseX from 'base-x'; 3 | 4 | const Base58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'; 5 | export const base58 = baseX(Base58); 6 | 7 | const Base58ValidCharacters = new Set(Base58); 8 | 9 | export function isBase58(s: string): boolean { 10 | for (let i = 0; i < s.length; i++) { 11 | if (!Base58ValidCharacters.has(s.charAt(i))) return false; 12 | } 13 | return true; 14 | } 15 | -------------------------------------------------------------------------------- /packages/config/src/color.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Parse a string as hex, return 0 on failure 3 | * @param str string to parse 4 | */ 5 | export function parseHex(str: string): number { 6 | if (str === '') return 0; 7 | const val = parseInt(str, 16); 8 | if (isNaN(val)) { 9 | throw new Error('Invalid hex byte: ' + str); 10 | } 11 | return val; 12 | } 13 | 14 | export interface Rgba { 15 | r: number; 16 | g: number; 17 | b: number; 18 | alpha: number; 19 | } 20 | 21 | /** 22 | * Parse a hexstring into RGBA 23 | * 24 | * Defaults to 0 if missing values 25 | * @param str string to parse 26 | */ 27 | export function parseRgba(str: string): Rgba { 28 | if (str.startsWith('0x')) str = str.slice(2); 29 | else if (str.startsWith('#')) str = str.slice(1); 30 | if (str.length !== 6 && str.length !== 8) { 31 | throw new Error('Invalid hex color: ' + str); 32 | } 33 | return { 34 | r: parseHex(str.substr(0, 2)), 35 | g: parseHex(str.substr(2, 2)), 36 | b: parseHex(str.substr(4, 2)), 37 | alpha: parseHex(str.substr(6, 2)), 38 | }; 39 | } 40 | -------------------------------------------------------------------------------- /packages/config/src/config/base.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | import { ConfigId } from '../index.js'; 4 | 5 | /** 6 | * Ensure a ID is prefixed with one of the configuration objects 7 | */ 8 | export const IdParser = z.string().refine((r) => ConfigId.getPrefix(r) != null); 9 | 10 | /** 11 | * Base interface for all dynamo records 12 | * 13 | * all records should have these values. 14 | */ 15 | export const ConfigBase = z.object({ 16 | /** 17 | * Primary key of the table 18 | * 19 | * Needs to be prefixed with the type {@link ConfigPrefix} 20 | * 21 | * @example 22 | * - "ts_aerial" 23 | * - "im_ada2b434dede4c64b782c1fd373bb0b9ac" 24 | */ 25 | id: IdParser, 26 | 27 | /** 28 | * Slug friendly name for the objects 29 | * 30 | * @example 31 | * - "gebco_2023-305m" 32 | * - "taranaki-2022-2023-0.1m" 33 | */ 34 | name: z.string(), 35 | 36 | /** 37 | * Timestamp when this object was last modified 38 | */ 39 | updatedAt: z.number().optional(), 40 | 41 | /** 42 | * Was this configuration object generated from another object 43 | * 44 | * @default undefined / false 45 | */ 46 | virtual: z.boolean().optional(), 47 | }); 48 | 49 | export type ConfigBase = z.infer; 50 | -------------------------------------------------------------------------------- /packages/config/src/config/config.bundle.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | import { ConfigBase } from './base.js'; 4 | 5 | export const ConfigBundleParser = ConfigBase.extend({ 6 | /** 7 | * path to the configuration bundle 8 | * 9 | * This should be a full URL 10 | * 11 | * @example 12 | * - "s3://linz-basemaps/config/config-latest.gz" 13 | */ 14 | path: z.string(), 15 | /** 16 | * sha256base58 hash of the configuration 17 | * 18 | * {@link sha256base58} 19 | * 20 | * @example 21 | * - "HPV7UAB97VZXMs7iryoPYksxRNEbbBsvroyvTak4vSjt" 22 | */ 23 | hash: z.string(), 24 | 25 | /** 26 | * Location to the assets are all the fonts and sprites, this is generally 27 | * a cotar {@link https://github.com/linz/cotar} 28 | * 29 | * @example 30 | * - "s3://linz-basemaps/assets/assets-HPV7UAB97VZXMs7iryoPYksxRNEbbBsvroyvTak4vSjt.tar.co" 31 | */ 32 | assets: z.string().optional(), 33 | }); 34 | 35 | export type ConfigBundle = z.infer; 36 | -------------------------------------------------------------------------------- /packages/config/src/config/prefix.ts: -------------------------------------------------------------------------------- 1 | export enum ConfigPrefix { 2 | /** Prefix for imagery {@link ConfigImagery} */ 3 | Imagery = 'im', 4 | /** Prefix for tile sets {@link ConfigTileSet} */ 5 | TileSet = 'ts', 6 | /** Prefix for provider {@link ConfigProvider} */ 7 | Provider = 'pv', 8 | /** Prefix for style {@link ConfigVectorStyle} */ 9 | Style = 'st', 10 | /** Configuration bundled into a single file {@link ConfigBundle} */ 11 | ConfigBundle = 'cb', 12 | } 13 | 14 | export const ConfigPrefixes: Set = new Set(Object.values(ConfigPrefix)); 15 | -------------------------------------------------------------------------------- /packages/config/src/config/provider.ts: -------------------------------------------------------------------------------- 1 | import { WmtsProvider } from '@basemaps/geo'; 2 | 3 | import { ConfigBase } from './base.js'; 4 | 5 | export type ConfigProvider = WmtsProvider & ConfigBase; 6 | -------------------------------------------------------------------------------- /packages/config/src/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | BaseConfigWriteableObject, 3 | BasemapsConfigObject, 4 | BasemapsConfigProvider, 5 | ConfigId, 6 | getAllImagery, 7 | } from './base.config.js'; 8 | export { base58, isBase58 } from './base58.js'; 9 | export { ensureBase58, sha256base58 } from './base58.node.js'; 10 | export { parseHex, parseRgba, Rgba } from './color.js'; 11 | export { ConfigBase as BaseConfig } from './config/base.js'; 12 | export { ConfigBundle } from './config/config.bundle.js'; 13 | export { ConfigImagery, ConfigImageryOverview, ImageryBandType, ImageryDataType } from './config/imagery.js'; 14 | export { ConfigPrefix } from './config/prefix.js'; 15 | export { ConfigProvider } from './config/provider.js'; 16 | export { 17 | ConfigLayer, 18 | ConfigTileSet, 19 | ConfigTileSetRaster, 20 | ConfigTileSetRasterOutput, 21 | ConfigTileSetVector, 22 | TileResizeKernel, 23 | TileSetType, 24 | } from './config/tile.set.js'; 25 | export { DefaultColorRamp, DefaultColorRampOutput, DefaultTerrainRgbOutput } from './config/tile.set.output.js'; 26 | export { ConfigTileSetOutputParser } from './config/tile.set.pipeline.js'; 27 | export { ConfigVectorStyle, Layer, SourceRaster, Sources, SourceVector, StyleJson } from './config/vector.style.js'; 28 | export { ConfigBundled, ConfigProviderMemory } from './memory/memory.config.js'; 29 | export { standardizeLayerName } from './name.convertor.js'; 30 | -------------------------------------------------------------------------------- /packages/config/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "outDir": "./build", 5 | "rootDir": "./src" 6 | }, 7 | "include": ["src"], 8 | "references": [{ "path": "../__tests__/tsconfig.json" }, { "path": "../geo/tsconfig.json" }] 9 | } 10 | -------------------------------------------------------------------------------- /packages/config/typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "entryPoints": ["./src/**/*.ts"], 3 | "exclude": ["./**/__tests__/*.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/geo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@basemaps/geo", 3 | "version": "8.0.0", 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/linz/basemaps.git", 7 | "directory": "packages/geo" 8 | }, 9 | "author": { 10 | "name": "Land Information New Zealand", 11 | "url": "https://linz.govt.nz", 12 | "organization": true 13 | }, 14 | "type": "module", 15 | "engines": { 16 | "node": ">=16.0.0" 17 | }, 18 | "license": "MIT", 19 | "main": "./build/index.js", 20 | "types": "./build/index.d.ts", 21 | "scripts": { 22 | "test": "node --test" 23 | }, 24 | "publishConfig": { 25 | "access": "public" 26 | }, 27 | "files": [ 28 | "build/" 29 | ], 30 | "devDependencies": { 31 | "@types/geojson": "^7946.0.7", 32 | "@types/proj4": "^2.5.2" 33 | }, 34 | "dependencies": { 35 | "@linzjs/geojson": "^8.0.0", 36 | "@linzjs/tile-matrix-set": "^0.0.1", 37 | "proj4": "^2.8.0" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/geo/src/formats.ts: -------------------------------------------------------------------------------- 1 | /** Image formats supported by basemaps */ 2 | export type ImageFormat = 'webp' | 'png' | 'jpeg' | 'avif'; 3 | 4 | /** Vector tile formats supported by basemaps */ 5 | export type VectorFormat = 'pbf'; 6 | 7 | /** Supported output formats */ 8 | export type OutputFormat = ImageFormat | VectorFormat; 9 | -------------------------------------------------------------------------------- /packages/geo/src/index.ts: -------------------------------------------------------------------------------- 1 | export { BoundingBox, Bounds, NamedBounds, Point, Size } from './bounds.js'; 2 | export { Epsg, EpsgCode } from './epsg.js'; 3 | export { ImageFormat, OutputFormat, VectorFormat } from './formats.js'; 4 | export * from './proj/projection.js'; 5 | export { ProjectionLoader } from './proj/projection.loader.js'; 6 | export { TileSetName, TileSetNameValues } from './proj/tile.set.name.js'; 7 | export { QuadKey } from './quad.key.js'; 8 | export { Simplify } from './simplify.js'; 9 | export { LocationSlug as LocationUrl, LonLat, LonLatZoom } from './slug.js'; 10 | export * from './stac/index.js'; 11 | export { AttributionCollection, AttributionItem, AttributionStac } from './stac/stac.attribution.js'; 12 | export { TileId } from './tile.js'; 13 | export { TileJson, TileJsonV3, TileJsonVectorLayer } from './tile.json/tile.json.js'; 14 | export { Tile, TileMatrixSet } from './tile.matrix.set.js'; 15 | export { Citm2000Tms } from './tms/citm2000.js'; 16 | export { GoogleTms } from './tms/google.js'; 17 | export { TileMatrixSets } from './tms/index.js'; 18 | export { Nztm2000QuadTms, Nztm2000Tms } from './tms/nztm2000.js'; 19 | export { WmtsProvider } from './wmts/wmts.js'; 20 | export { getXyOrder } from './xy.order.js'; 21 | export { TileMatrixSetType, TileMatrixType } from '@linzjs/tile-matrix-set'; 22 | -------------------------------------------------------------------------------- /packages/geo/src/proj/citm2000.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Well known text for NZGD2000 / Chatham Islands TM 2000 3 | * 4 | * @see https://epsg.io/3793 5 | */ 6 | export const Citm2000 = `PROJCS["NZGD2000 / Chatham Islands TM 2000", 7 | GEOGCS["NZGD2000", 8 | DATUM["New_Zealand_Geodetic_Datum_2000", 9 | SPHEROID["GRS 1980",6378137,298.257222101, 10 | AUTHORITY["EPSG","7019"]], 11 | TOWGS84[0,0,0,0,0,0,0], 12 | AUTHORITY["EPSG","6167"]], 13 | PRIMEM["Greenwich",0, 14 | AUTHORITY["EPSG","8901"]], 15 | UNIT["degree",0.01745329251994328, 16 | AUTHORITY["EPSG","9122"]], 17 | AUTHORITY["EPSG","4167"]], 18 | UNIT["metre",1, 19 | AUTHORITY["EPSG","9001"]], 20 | PROJECTION["Transverse_Mercator"], 21 | PARAMETER["latitude_of_origin",0], 22 | PARAMETER["central_meridian",-176.5], 23 | PARAMETER["scale_factor",1], 24 | PARAMETER["false_easting",3500000], 25 | PARAMETER["false_northing",10000000], 26 | AUTHORITY["EPSG","3793"], 27 | AXIS["Easting",EAST], 28 | AXIS["Northing",NORTH]]`; 29 | -------------------------------------------------------------------------------- /packages/geo/src/proj/nztm2000.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Well known text for NZGD2000 3 | * 4 | * @see https://epsg.io/2193 5 | */ 6 | export const Nztm2000 = `PROJCS["NZGD2000 / New Zealand Transverse Mercator 2000", 7 | GEOGCS["NZGD2000", 8 | DATUM["New_Zealand_Geodetic_Datum_2000", 9 | SPHEROID["GRS 1980",6378137,298.257222101, 10 | AUTHORITY["EPSG","7019"]], 11 | TOWGS84[0,0,0,0,0,0,0], 12 | AUTHORITY["EPSG","6167"]], 13 | PRIMEM["Greenwich",0, 14 | AUTHORITY["EPSG","8901"]], 15 | UNIT["degree",0.0174532925199433, 16 | AUTHORITY["EPSG","9122"]], 17 | AUTHORITY["EPSG","4167"]], 18 | PROJECTION["Transverse_Mercator"], 19 | PARAMETER["latitude_of_origin",0], 20 | PARAMETER["central_meridian",173], 21 | PARAMETER["scale_factor",0.9996], 22 | PARAMETER["false_easting",1600000], 23 | PARAMETER["false_northing",10000000], 24 | UNIT["metre",1, 25 | AUTHORITY["EPSG","9001"]], 26 | AUTHORITY["EPSG","2193"]]`; 27 | -------------------------------------------------------------------------------- /packages/geo/src/proj/projection.loader.ts: -------------------------------------------------------------------------------- 1 | import { Epsg } from '../epsg.js'; 2 | import { Projection } from './projection.js'; 3 | 4 | declare const fetch: (r: string) => Promise<{ ok: boolean; text: () => Promise }>; 5 | 6 | export class ProjectionLoader { 7 | // Exposed for testing 8 | static _fetch = fetch; 9 | 10 | /** 11 | * Ensure that a projection EPSG code is avialable for use in Proj4js 12 | * 13 | * If its not already loaded, lookup definition from spatialreference.org 14 | * @param code 15 | */ 16 | static async load(code: number): Promise { 17 | if (Projection.tryGet(code) != null) return Epsg.get(code); 18 | const url = `https://spatialreference.org/ref/epsg/${code}/ogcwkt/`; 19 | 20 | const res = await this._fetch(url); 21 | if (!res.ok) throw new Error('Failed to load projection information for:' + code); 22 | 23 | let epsg = Epsg.tryGet(code); 24 | if (epsg == null) epsg = new Epsg(code); 25 | 26 | const text = await res.text(); 27 | Projection.define(epsg, text); 28 | return epsg; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/geo/src/proj/tile.set.name.ts: -------------------------------------------------------------------------------- 1 | export enum TileSetName { 2 | aerial = 'aerial', 3 | } 4 | 5 | export function TileSetNameValues(): TileSetName[] { 6 | return [TileSetName.aerial]; 7 | } 8 | -------------------------------------------------------------------------------- /packages/geo/src/tile.ts: -------------------------------------------------------------------------------- 1 | import { Tile } from './tile.matrix.set.js'; 2 | 3 | export const TileId = { 4 | /** Create a tile from a tile ID in the format `:z-:x-:y` */ 5 | toTile(tileId: string): Tile { 6 | const parts = tileId.split('-'); 7 | if (parts.length !== 3) throw new Error('Invalid TileId: ' + tileId); 8 | const tile: Tile = { z: Number(parts[0]), x: Number(parts[1]), y: Number(parts[2]) }; 9 | if (isNaN(tile.x) || isNaN(tile.y) || isNaN(tile.z)) throw new Error('Tile is not a number: ' + tileId); 10 | return tile; 11 | }, 12 | /** Create a tileID `:z-:x-:y */ 13 | fromTile(tile: Tile): string { 14 | return `${tile.z}-${tile.x}-${tile.y}`; 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /packages/geo/src/tms/nztm2000.ts: -------------------------------------------------------------------------------- 1 | import * as Nztm2000 from '@linzjs/tile-matrix-set'; 2 | 3 | import { TileMatrixSet } from '../tile.matrix.set.js'; 4 | 5 | export const Nztm2000Tms = new TileMatrixSet(Nztm2000.Nztm2000); 6 | export const Nztm2000QuadTms = new TileMatrixSet(Nztm2000.Nztm2000Quad); 7 | -------------------------------------------------------------------------------- /packages/geo/src/wmts/wmts.ts: -------------------------------------------------------------------------------- 1 | /** WMTS Provider information */ 2 | export interface WmtsProvider { 3 | version: number; 4 | serviceIdentification: { 5 | title: string; 6 | description: string; 7 | fees: string; 8 | accessConstraints: string; 9 | }; 10 | serviceProvider: { 11 | name: string; 12 | site: string; 13 | contact: { 14 | individualName: string; 15 | position: string; 16 | phone: string; 17 | address: { 18 | deliveryPoint: string; 19 | city: string; 20 | postalCode: string; 21 | country: string; 22 | email: string; 23 | }; 24 | }; 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /packages/geo/src/xy.order.ts: -------------------------------------------------------------------------------- 1 | import { Epsg, EpsgCode } from './epsg.js'; 2 | 3 | /** 4 | * Order of X & Y coordinates when defined as a array 5 | * 6 | * `[x,y]` vs `[y,x]` 7 | */ 8 | export type XyOrder = 'xy' | 'yx'; 9 | 10 | /** 11 | * Get the X & Y coordinate order for a given EPSG 12 | * @param epsg EPSG to lookup 13 | */ 14 | export function getXyOrder(epsg: Epsg | EpsgCode): XyOrder { 15 | const code = typeof epsg === 'number' ? epsg : epsg.code; 16 | /** 17 | * - [EPSG:2193](https://www.opengis.net/def/crs/EPSG/0/2193) (NZTM) is defined in [y, x] 18 | * - [EPSG:3793](https://www.opengis.net/def/crs/EPSG/0/3793) (CITM) is defined in [y, x] 19 | * specified by the coordinate system [cs:4500](https://www.opengis.net/def/cs/EPSG/0/4500) 20 | */ 21 | if (code === EpsgCode.Nztm2000 || code === EpsgCode.Citm2000) return 'yx'; 22 | 23 | // TODO there are other projections that are YX, 24 | // TileMatrixSet v2 specification includes Xy ordering, https://docs.ogc.org/is/17-083r4/21-066r1.html#_adding_optional_orderedaxes_to_highlight_crs_axis_ordering 25 | return 'xy'; 26 | } 27 | -------------------------------------------------------------------------------- /packages/geo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | 4 | "compilerOptions": { 5 | "outDir": "./build", 6 | "rootDir": "./src" 7 | }, 8 | "include": ["src"], 9 | "references": [{ "path": "../__tests__/tsconfig.json" }, { "path": "../linzjs-geojson/tsconfig.json" }] 10 | } 11 | -------------------------------------------------------------------------------- /packages/geo/typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "entryPoints": ["./src/**/*.ts"], 3 | "exclude": ["./**/__tests__/*.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/lambda-analytic-cloudfront/README.md: -------------------------------------------------------------------------------- 1 | # @basemaps/lambda-analytic-cloudfront 2 | 3 | Generate analytics from CloudFront distribution statistics 4 | 5 | Every hour this lambda function runs and generates a rolled up summary of usage by API Key 6 | -------------------------------------------------------------------------------- /packages/lambda-analytic-cloudfront/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@basemaps/lambda-analytic-cloudfront", 3 | "version": "8.1.0", 4 | "private": true, 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/linz/basemaps.git", 8 | "directory": "packages/lambda-analytic-cloudfront" 9 | }, 10 | "author": { 11 | "name": "Land Information New Zealand", 12 | "url": "https://linz.govt.nz", 13 | "organization": true 14 | }, 15 | "type": "module", 16 | "engines": { 17 | "node": ">=16.0.0" 18 | }, 19 | "license": "MIT", 20 | "dependencies": { 21 | "@basemaps/config": "^8.1.0", 22 | "@basemaps/geo": "^8.0.0", 23 | "@basemaps/shared": "^8.1.0", 24 | "@elastic/elasticsearch": "^8.16.2", 25 | "@linzjs/lambda": "^4.0.0", 26 | "ua-parser-js": "^1.0.39" 27 | }, 28 | "scripts": { 29 | "test": "node --test", 30 | "bundle": "../../scripts/bundle.mjs package.json" 31 | }, 32 | "devDependencies": { 33 | "@types/ua-parser-js": "^0.7.36" 34 | }, 35 | "bundle": { 36 | "entry": "src/index.ts", 37 | "outdir": "dist/", 38 | "external": [ 39 | "pino-pretty" 40 | ] 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/lambda-analytic-cloudfront/src/bin.ts: -------------------------------------------------------------------------------- 1 | import { LogConfig } from '@basemaps/shared'; 2 | import { LambdaRequest } from '@linzjs/lambda'; 3 | import { Context } from 'aws-lambda'; 4 | 5 | import { main } from './handler.js'; 6 | 7 | /** 8 | * Manually run the lambda function, this can be helpful for debugging the analytic roll up process 9 | */ 10 | main(new LambdaRequest(null, {} as Context, LogConfig.get())).catch((e) => console.error(e)); 11 | -------------------------------------------------------------------------------- /packages/lambda-analytic-cloudfront/src/date.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Create a UTC time date that is rounded down to one hour ago 3 | * 4 | * @example 5 | * 2025-03-17T03:19:33.599Z -> 2025-03-17T02:00:00.000Z 6 | * 7 | * @returns 8 | */ 9 | export function getOneHourAgo(): Date { 10 | const maxDate = new Date(); 11 | maxDate.setUTCMinutes(0); 12 | maxDate.setUTCSeconds(0); 13 | maxDate.setUTCMilliseconds(0); 14 | maxDate.setUTCHours(maxDate.getUTCHours() - 1); 15 | return maxDate; 16 | } 17 | 18 | export function* byDay(startDate: Date, endDate: Date): Generator { 19 | const currentDate = new Date(startDate); 20 | currentDate.setUTCMinutes(0); 21 | currentDate.setUTCSeconds(0); 22 | currentDate.setUTCMilliseconds(0); 23 | while (true) { 24 | yield currentDate.toISOString().slice(0, 10); 25 | currentDate.setUTCDate(currentDate.getUTCDate() - 1); 26 | if (currentDate.getTime() < endDate.getTime()) break; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/lambda-analytic-cloudfront/src/index.ts: -------------------------------------------------------------------------------- 1 | import { LogConfig } from '@basemaps/shared'; 2 | import { lf } from '@linzjs/lambda'; 3 | 4 | import { main } from './handler.js'; 5 | 6 | export const handler = lf.handler(main, { tracePercent: 0, rejectOnError: true }, LogConfig.get()); 7 | -------------------------------------------------------------------------------- /packages/lambda-analytic-cloudfront/src/log/query.ts: -------------------------------------------------------------------------------- 1 | export interface QueryStringInfo { 2 | api: string; 3 | pipeline?: string; 4 | } 5 | function getQuery(str: string): QueryStringInfo { 6 | const urlSearch = new URLSearchParams(str); 7 | const api = _getApi(urlSearch); 8 | const pipeline = urlSearch.get('pipeline') ?? undefined; 9 | return { api, pipeline }; 10 | } 11 | 12 | function _getApi(url: URLSearchParams): string { 13 | const api = url.get('api') ?? ''; 14 | // api keys are 27 chars starting with d or c 15 | if (api.length !== 27) return 'invalid'; 16 | if (api.startsWith('d')) return api; 17 | if (api.startsWith('c')) return api; 18 | return 'invalid'; 19 | } 20 | const QueryMap = new Map(); 21 | 22 | export function parseQueryString(str: string): QueryStringInfo { 23 | let existing = QueryMap.get(str); 24 | if (existing == null) { 25 | existing = getQuery(str); 26 | QueryMap.set(str, existing); 27 | } 28 | // This can get very very large so periodically clear it 29 | if (QueryMap.size > 5_000_000) QueryMap.clear(); 30 | return existing; 31 | } 32 | -------------------------------------------------------------------------------- /packages/lambda-analytic-cloudfront/src/log/referer.ts: -------------------------------------------------------------------------------- 1 | const hostCache = new Map(); 2 | 3 | export function getUrlHost(ref: string): string { 4 | let existing = hostCache.get(ref); 5 | if (existing == null) { 6 | existing = _getUrlHost(ref); 7 | hostCache.set(ref, existing); 8 | } 9 | return existing; 10 | } 11 | /** Extract the hostname from a url */ 12 | export function _getUrlHost(ref: string): string { 13 | if (ref == null) return 'unknown'; 14 | if (ref === '-') return 'unknown'; 15 | 16 | try { 17 | const { hostname } = new URL(ref); 18 | if (hostname == null) return ref; 19 | if (hostname.startsWith('www.')) return hostname.slice(4); 20 | return hostname; 21 | } catch (e) { 22 | if (!ref.startsWith('http')) return _getUrlHost('https://' + ref); 23 | // Ignore invalid referer hostname 24 | // eslint-disable-next-line no-console 25 | console.log(ref); 26 | } 27 | return 'unknown'; 28 | } 29 | -------------------------------------------------------------------------------- /packages/lambda-analytic-cloudfront/src/useragent/agent.ts: -------------------------------------------------------------------------------- 1 | import { Gis } from './agents/gis.js'; 2 | import { Bot, Programming } from './agents/programming.js'; 3 | import { UserAgentParsers } from './parser.js'; 4 | 5 | export const UaParser = new UserAgentParsers(); 6 | 7 | Object.entries(Programming).forEach(([key, create]) => UaParser.addParser(key, create)); 8 | Object.entries(Gis).forEach(([key, create]) => UaParser.addParser(key, create)); 9 | Object.entries(Bot).forEach(([key, create]) => UaParser.addParser(key, create)); 10 | -------------------------------------------------------------------------------- /packages/lambda-analytic-cloudfront/src/useragent/parser.types.ts: -------------------------------------------------------------------------------- 1 | export type UserAgentParser = (ua: string) => UserAgentInfo | undefined; 2 | export interface UserAgentInfo { 3 | name: string; 4 | variant?: string; 5 | version?: string; 6 | os?: UserAgentOs; 7 | } 8 | 9 | export type UserAgentOs = 'windows' | 'macos' | 'ios' | 'android' | 'linux' | 'unknown'; 10 | export const ValidOs = new Set(['windows', 'macos', 'ios', 'android', 'linux']); 11 | 12 | export function isValidOs(os: string): os is UserAgentOs { 13 | return ValidOs.has(os); 14 | } 15 | -------------------------------------------------------------------------------- /packages/lambda-analytic-cloudfront/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | 4 | "compilerOptions": { 5 | "outDir": "./build", 6 | "rootDir": "./src" 7 | }, 8 | "include": ["src"], 9 | "references": [{ "path": "../config" }, { "path": "../geo" }, { "path": "../shared" }] 10 | } 11 | -------------------------------------------------------------------------------- /packages/lambda-analytic-cloudfront/typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "entryPoints": ["./src/**/*.ts"], 3 | "exclude": ["./**/__tests__/*.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/lambda-analytics/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@basemaps/lambda-analytics", 3 | "version": "8.1.0", 4 | "private": true, 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/linz/basemaps.git", 8 | "directory": "packages/lambda-analytics" 9 | }, 10 | "author": { 11 | "name": "Land Information New Zealand", 12 | "url": "https://linz.govt.nz", 13 | "organization": true 14 | }, 15 | "type": "module", 16 | "engines": { 17 | "node": ">=16.0.0" 18 | }, 19 | "license": "MIT", 20 | "dependencies": { 21 | "@basemaps/config": "^8.1.0", 22 | "@basemaps/geo": "^8.0.0", 23 | "@basemaps/shared": "^8.1.0", 24 | "ua-parser-js": "^1.0.2" 25 | }, 26 | "scripts": { 27 | "test": "node --test", 28 | "bundle": "../../scripts/bundle.mjs package.json" 29 | }, 30 | "devDependencies": { 31 | "@types/ua-parser-js": "^0.7.36" 32 | }, 33 | "bundle": { 34 | "entry": "src/index.ts", 35 | "outdir": "dist/", 36 | "external": [ 37 | "pino-pretty" 38 | ] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/lambda-analytics/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | 4 | "compilerOptions": { 5 | "outDir": "./build", 6 | "rootDir": "./src" 7 | }, 8 | "include": ["src"], 9 | "references": [{ "path": "../config" }, { "path": "../geo" }, { "path": "../shared" }] 10 | } 11 | -------------------------------------------------------------------------------- /packages/lambda-analytics/typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "entryPoints": ["./src/**/*.ts"], 3 | "exclude": ["./**/__tests__/*.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/lambda-tiler/bundle.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Create a deployment bundle with sharp/libvips prebuilt included 4 | # 5 | ../../scripts/bundle.mjs package.json 6 | cd dist 7 | ../scripts/create.deployment.package.mjs 8 | # Make the new package a commonjs module 9 | cp -r ../static . 10 | # @see https://sharp.pixelplumbing.com/en/stable/install/#aws-lambda 11 | npm install --cpu=arm64 --arch=arm64 --platform=linux --omit=dev 12 | -------------------------------------------------------------------------------- /packages/lambda-tiler/scripts/create.deployment.package.mjs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * Create a package.json for the `dist` bundle based off the parent package.json 4 | * 5 | * This is needed as libsharp has to be "installed" into the dist node_modules so that lambda has access to sharp 6 | */ 7 | import * as fs from 'fs'; 8 | 9 | const parentPackage = JSON.parse(fs.readFileSync('../package.json').toString()); 10 | 11 | // Find the exact version of a package in the package-lock lock 12 | export function getPackageVersion(packageName) { 13 | const parentLock = JSON.parse(fs.readFileSync('../../../package-lock.json').toString()); 14 | return parentLock.packages['node_modules/' + packageName].version; 15 | } 16 | 17 | // the bundle is a commonjs module 18 | parentPackage.type = 'commonjs'; 19 | parentPackage.main = 'index.js'; 20 | parentPackage.dependencies = { sharp: getPackageVersion('sharp'), lerc: getPackageVersion('lerc') }; 21 | 22 | // Clean up 23 | delete parentPackage.types; 24 | delete parentPackage.devDependencies; 25 | 26 | console.log('Installing dependencies', parentPackage.dependencies); 27 | fs.writeFileSync('./package.json', JSON.stringify(parentPackage, null, 2)); 28 | -------------------------------------------------------------------------------- /packages/lambda-tiler/src/routes/__tests__/memory.fs.ts: -------------------------------------------------------------------------------- 1 | import { Readable } from 'stream'; 2 | 3 | export async function toBuffer(stream: Readable): Promise { 4 | return new Promise((resolve, reject) => { 5 | const buf: Buffer[] = []; 6 | 7 | stream.on('data', (chunk: Buffer) => buf.push(chunk)); 8 | stream.on('end', () => resolve(Buffer.concat(buf))); 9 | stream.on('error', (err) => reject(`error converting stream - ${String(err)}`)); 10 | }); 11 | } 12 | export function toReadable(r: string | Buffer | Readable): Readable { 13 | if (typeof r === 'string') r = Buffer.from(r); 14 | return Readable.from(r); 15 | } 16 | -------------------------------------------------------------------------------- /packages/lambda-tiler/src/routes/fonts.ts: -------------------------------------------------------------------------------- 1 | import { LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda'; 2 | 3 | import { assetProvider } from '../util/assets.provider.js'; 4 | 5 | interface FontGet { 6 | Params: { fontStack: string; range: string }; 7 | } 8 | 9 | export async function fontGet(req: LambdaHttpRequest): Promise { 10 | const targetFile = `fonts/${req.params.fontStack}/${req.params.range}.pbf`; 11 | return assetProvider.serve(req, targetFile, 'application/x-protobuf'); 12 | } 13 | 14 | export async function fontList(req: LambdaHttpRequest): Promise { 15 | return assetProvider.serve(req, 'fonts/fonts.json', 'application/json'); 16 | } 17 | -------------------------------------------------------------------------------- /packages/lambda-tiler/src/routes/ping.ts: -------------------------------------------------------------------------------- 1 | import { HttpHeader, LambdaHttpResponse } from '@linzjs/lambda'; 2 | 3 | const OkResponse = new LambdaHttpResponse(200, 'ok'); 4 | OkResponse.header(HttpHeader.CacheControl, 'no-store'); 5 | 6 | export function pingGet(): Promise { 7 | return Promise.resolve(OkResponse); 8 | } 9 | -------------------------------------------------------------------------------- /packages/lambda-tiler/src/routes/sprites.ts: -------------------------------------------------------------------------------- 1 | import { LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda'; 2 | import path from 'path'; 3 | 4 | import { assetProvider } from '../util/assets.provider.js'; 5 | import { NotFound } from '../util/response.js'; 6 | 7 | interface SpriteGet { 8 | Params: { 9 | spriteName: string; 10 | }; 11 | } 12 | 13 | const Extensions = new Map(); 14 | Extensions.set('.png', 'image/png'); 15 | Extensions.set('.json', 'application/json'); 16 | 17 | export async function spriteGet(req: LambdaHttpRequest): Promise { 18 | const extension = path.extname(req.params.spriteName); 19 | const mimeType = Extensions.get(extension); 20 | if (mimeType == null) return NotFound(); 21 | 22 | return assetProvider.serve(req, `sprites/${req.params.spriteName}`, mimeType); 23 | } 24 | -------------------------------------------------------------------------------- /packages/lambda-tiler/src/util/__test__/cache.test.ts: -------------------------------------------------------------------------------- 1 | import assert from 'node:assert'; 2 | import { describe, it } from 'node:test'; 3 | 4 | import { fsa, FsMemory } from '@chunkd/fs'; 5 | 6 | import { SourceCache } from '../source.cache.js'; 7 | 8 | describe('CoSourceCache', () => { 9 | it('should not exit if a promise rejection happens for tiff', async () => { 10 | const cache = new SourceCache(5); 11 | 12 | const mem = new FsMemory(); 13 | const tiffLoc = new URL('memory://foo/bar.tiff'); 14 | await mem.write(tiffLoc, Buffer.from('ABC123')); 15 | fsa.register('memory://', mem); 16 | 17 | let failCount = 0; 18 | await cache.getCog(tiffLoc).catch(() => failCount++); 19 | assert.equal(cache.cache.currentSize, 0); 20 | assert.equal(failCount, 1); 21 | }); 22 | 23 | it('should not exit if a promise rejection happens for tar', async () => { 24 | const cache = new SourceCache(5); 25 | 26 | const mem = new FsMemory(); 27 | const tiffLoc = new URL('memory://foo/bar.tar'); 28 | await mem.write(tiffLoc, Buffer.from('ABC123')); 29 | fsa.register('memory://', mem); 30 | 31 | let failCount = 0; 32 | await cache.getCotar(tiffLoc).catch(() => failCount++); 33 | assert.equal(cache.cache.currentSize, 0); 34 | assert.equal(failCount, 1); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /packages/lambda-tiler/src/util/etag.ts: -------------------------------------------------------------------------------- 1 | import { sha256base58 } from '@basemaps/config'; 2 | import { HttpHeader, LambdaHttpRequest } from '@linzjs/lambda'; 3 | 4 | export const Etag = { 5 | key(obj: unknown): string { 6 | if (Buffer.isBuffer(obj) || typeof obj === 'string') return sha256base58(obj); 7 | return sha256base58(JSON.stringify(obj)); 8 | }, 9 | 10 | isNotModified(req: LambdaHttpRequest, cacheKey: string): boolean { 11 | // If the user has supplied a IfNoneMatch Header and it contains the full sha256 sum for our 12 | // etag this tile has not been modified. 13 | const ifNoneMatch = req.header(HttpHeader.IfNoneMatch); 14 | if (ifNoneMatch != null && ifNoneMatch.indexOf(cacheKey) > -1) { 15 | req.set('cache', { hit: true, match: ifNoneMatch }); 16 | return true; 17 | } 18 | return false; 19 | }, 20 | }; 21 | -------------------------------------------------------------------------------- /packages/lambda-tiler/src/util/response.ts: -------------------------------------------------------------------------------- 1 | import { LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda'; 2 | 3 | export const NotFound = (msg: string = 'Not Found'): LambdaHttpResponse => new LambdaHttpResponse(404, msg); 4 | export const NotModified = (): LambdaHttpResponse => new LambdaHttpResponse(304, 'Not modified'); 5 | export const NoContent = (): LambdaHttpResponse => new LambdaHttpResponse(204, 'No Content'); 6 | export const OkResponse = (req: LambdaHttpRequest): LambdaHttpResponse => 7 | new LambdaHttpResponse(200, 'ok').json({ id: req.id, correlationId: req.correlationId, message: 'ok' }); 8 | -------------------------------------------------------------------------------- /packages/lambda-tiler/static/expected_tile_2193_153_255_z7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/packages/lambda-tiler/static/expected_tile_2193_153_255_z7.png -------------------------------------------------------------------------------- /packages/lambda-tiler/static/expected_tile_NZTM2000Quad_30_33_z6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/packages/lambda-tiler/static/expected_tile_NZTM2000Quad_30_33_z6.png -------------------------------------------------------------------------------- /packages/lambda-tiler/static/expected_tile_WebMercatorQuad_252_156_z8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/packages/lambda-tiler/static/expected_tile_WebMercatorQuad_252_156_z8.png -------------------------------------------------------------------------------- /packages/lambda-tiler/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "outDir": "./build", 5 | "rootDir": "./src" 6 | }, 7 | "include": ["src"], 8 | "references": [ 9 | { "path": "../config/tsconfig.json" }, 10 | { "path": "../config-loader/tsconfig.json" }, 11 | { "path": "../shared/tsconfig.json" }, 12 | { "path": "../geo/tsconfig.json" }, 13 | { "path": "../tiler/tsconfig.json" }, 14 | { "path": "../tiler-sharp/tsconfig.json" }, 15 | { "path": "../attribution/tsconfig.json" } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /packages/lambda-tiler/typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "entryPoints": ["./src/**/*.ts"], 3 | "exclude": ["./**/__tests__/*.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/landing/README.md: -------------------------------------------------------------------------------- 1 | # @basemaps/landing 2 | 3 | The landing page for basemaps https://basemaps.linz.govt.nz 4 | 5 | ## Development 6 | 7 | To start a local test server there are two options 8 | 9 | ### Create a full basemaps server 10 | 11 | Using [@basemaps/server](../server/README.md) a entire local test environment can be run. to ensure the latest assets are served by the local server `npm run bundle` should be run first 12 | 13 | this is best when testing larger basemaps changes across multiple packages, but needs access to some imagery. 14 | 15 | ### Simple local server 16 | 17 | The simple local server is used to validate html/css/js changes in the landing page this does not run a basemaps tile server and will default to `https://dev.basemaps.linz.govt.nz` 18 | 19 | ```bash 20 | npm run bundle 21 | node serve.mjs 22 | ``` 23 | -------------------------------------------------------------------------------- /packages/landing/serve.mjs: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs'; 2 | import http from 'node:http'; 3 | import path from 'node:path'; 4 | 5 | import mime from 'mime-types'; 6 | 7 | /** 8 | * Very basic http server that redirects all 404's back to index.html 9 | * as basemaps uses `/@location` for its main page these `/@*` has to be served with `index.html` 10 | */ 11 | const srv = http.createServer(function (request, response) { 12 | const target = path.join('dist', request.url); 13 | const stat = fs.existsSync(target); 14 | if (!request.url.endsWith('/') && stat) { 15 | const type = mime.lookup(target); 16 | response.statusCode = 200; 17 | response.setHeader('content-type', type); 18 | 19 | fs.createReadStream(target, { 20 | bufferSize: 4 * 1024, 21 | }).pipe(response); 22 | console.log(new Date().toISOString(), 200, type, request.url); 23 | return; 24 | } 25 | 26 | response.statusCode = 200; 27 | fs.createReadStream('dist/index.html', { 28 | bufferSize: 4 * 1024, 29 | }).pipe(response); 30 | }); 31 | 32 | const port = process.env.PORT ? Number(process.env.PORT) : 5000; 33 | srv.listen(port, () => { 34 | process.stdout.write(`listening.. http://localhost:${port}\n`); 35 | }); 36 | -------------------------------------------------------------------------------- /packages/landing/src/__tests__/NZTMTileLocation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linz/basemaps/8cbef0b0a9ef3db804d05b533b6858f55c9064c9/packages/landing/src/__tests__/NZTMTileLocation.png -------------------------------------------------------------------------------- /packages/landing/src/__tests__/config.debug.test.ts: -------------------------------------------------------------------------------- 1 | import assert from 'node:assert'; 2 | import { describe, it } from 'node:test'; 3 | 4 | import { ConfigDebug, DebugDefaults, DebugState } from '../config.debug.js'; 5 | 6 | function urlToString(o: Partial): string { 7 | const url = new URLSearchParams(); 8 | ConfigDebug.toUrl(o as DebugState, url); 9 | return url.toString(); 10 | } 11 | 12 | describe('ConfigDebug', () => { 13 | it('should only serialize when not defaults', () => { 14 | assert.equal(urlToString(DebugDefaults), ''); 15 | }); 16 | 17 | it('should write to url', () => { 18 | assert.equal(urlToString({ debug: true }), 'debug=true'); 19 | assert.equal(urlToString({ debug: false }), ''); 20 | assert.equal(urlToString({ debug: true, 'debug.background': 'magenta' }), 'debug=true&debug.background=magenta'); 21 | }); 22 | 23 | it('should round trip', () => { 24 | const cfg = { ...DebugDefaults }; 25 | cfg.debug = true; 26 | cfg['debug.background'] = 'magenta'; 27 | cfg['debug.layer.linz-topographic'] = 0.5; 28 | cfg['debug.layer.linz-aerial'] = -1; 29 | 30 | const url = urlToString(cfg); 31 | const newCfg = { ...DebugDefaults }; 32 | ConfigDebug.fromUrl(newCfg, new URLSearchParams(`?${url}`)); 33 | assert.deepEqual(newCfg, cfg); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /packages/landing/src/__tests__/config.test.ts: -------------------------------------------------------------------------------- 1 | import assert from 'node:assert'; 2 | import { describe, it } from 'node:test'; 3 | 4 | import { Config } from '../config.js'; 5 | 6 | describe('Config', () => { 7 | it('should return the same api key', () => { 8 | const keyA = Config.ApiKey; 9 | assert.equal(keyA, Config.ApiKey); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /packages/landing/src/components/link.tsx: -------------------------------------------------------------------------------- 1 | import { Component, ReactNode } from 'react'; 2 | 3 | interface IconProps { 4 | name: string; 5 | } 6 | export class Icon extends Component { 7 | override render(): ReactNode { 8 | return {this.props.name}; 9 | } 10 | } 11 | 12 | interface LinkProps { 13 | href: string; 14 | icon?: string; 15 | ariaLabel?: string; 16 | children: ReactNode[] | ReactNode; 17 | } 18 | export class Link extends Component { 19 | override render(): ReactNode { 20 | return ( 21 | 28 | {this.props.children} 29 | {this.props.icon ? : undefined} 30 | 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/landing/src/global.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | 3 | declare global { 4 | interface Window { 5 | // Google analytics 6 | dataLayer: unknown[]; 7 | gtag(...args: unknown[]): void; 8 | 9 | // Expose for testing 10 | MaplibreMap?: maplibregl.Map; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/landing/src/index.tsx: -------------------------------------------------------------------------------- 1 | import { Component, Fragment, ReactNode } from 'react'; 2 | import { createRoot } from 'react-dom/client'; 3 | 4 | import { Footer } from './components/layout.footer.js'; 5 | import { Header } from './components/layout.header.js'; 6 | import { Basemaps } from './components/map.js'; 7 | import { NewFeature } from './components/new-features/3d.map.js'; 8 | import { Config } from './config.js'; 9 | import { WindowUrl } from './url.js'; 10 | import { isWebpSupported } from './webp.js'; 11 | 12 | class Page extends Component { 13 | override render(): ReactNode { 14 | return ( 15 | 16 |
17 | 18 | 19 |