Compare commits
20 commits
Author | SHA1 | Date | |
---|---|---|---|
4b1ffd489f | |||
![]() |
cd613e2fb5 | ||
![]() |
3e521a7d92 | ||
![]() |
d88fbb7c55 | ||
![]() |
6d4ab40b96 | ||
![]() |
5441a10488 | ||
![]() |
8a7d9957fb | ||
![]() |
6d1c617752 | ||
![]() |
3881219fee | ||
![]() |
3110cab18f | ||
![]() |
f2b48acb61 | ||
![]() |
6e0006ffcf | ||
![]() |
6ef12fba6c | ||
![]() |
3d8bf78974 | ||
![]() |
1d60dd6afc | ||
![]() |
7662cb84ce | ||
![]() |
467203e125 | ||
![]() |
9bb270b6c5 | ||
![]() |
f02c63c94c | ||
![]() |
b0a2cefb0e |
36 changed files with 4088 additions and 5409 deletions
1
.github/workflows/automerger.yml
vendored
1
.github/workflows/automerger.yml
vendored
|
@ -14,3 +14,4 @@ jobs:
|
|||
- uses: fastify/github-action-merge-dependabot@v3
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
target: minor
|
||||
|
|
5
.github/workflows/ci.yml
vendored
5
.github/workflows/ci.yml
vendored
|
@ -21,6 +21,11 @@ jobs:
|
|||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: Install dependencies (Ubuntu) 🚀
|
||||
run: >-
|
||||
sudo apt-get install -qq libcairo2-dev libjpeg8-dev libpango1.0-dev
|
||||
libgif-dev build-essential
|
||||
|
||||
- name: Setup node env 📦
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
|
|
18
.github/workflows/ct.yml
vendored
18
.github/workflows/ct.yml
vendored
|
@ -65,6 +65,22 @@ jobs:
|
|||
context: .
|
||||
push: false
|
||||
platforms: linux/arm64,linux/amd64
|
||||
# experimental: https://github.com/docker/build-push-action/blob/master/docs/advanced/cache.md#cache-backend-api
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Create Tileserver Light Directory
|
||||
run: node publish.js --no-publish
|
||||
|
||||
- name: Install node dependencies
|
||||
run: npm ci --prefer-offline --no-audit
|
||||
working-directory: ./light
|
||||
|
||||
- name: Test Light Version to Docker Hub
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: ./light
|
||||
file: ./light/Dockerfile
|
||||
push: false
|
||||
platforms: linux/arm64,linux/amd64
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
|
11
.gitignore
vendored
11
.gitignore
vendored
|
@ -1,4 +1,15 @@
|
|||
docs/_build
|
||||
public/resources/leaflet-hash.js
|
||||
public/resources/leaflet.css
|
||||
public/resources/leaflet.js
|
||||
public/resources/leaflet.js.map
|
||||
public/resources/mapbox-gl-rtl-text.js
|
||||
public/resources/maplibre-gl-inspect.css
|
||||
public/resources/maplibre-gl-inspect.js
|
||||
public/resources/maplibre-gl-inspect.js.map
|
||||
public/resources/maplibre-gl.css
|
||||
public/resources/maplibre-gl.js
|
||||
public/resources/maplibre-gl.js.map
|
||||
node_modules
|
||||
test_data
|
||||
test_data.zip
|
||||
|
|
18
CHANGELOG.md
18
CHANGELOG.md
|
@ -1,7 +1,23 @@
|
|||
# tileserver-gl changelog
|
||||
|
||||
## 5.2.0
|
||||
* Use npm packages for public/resources (https://github.com/maptiler/tileserver-gl/pull/1427) by @okimiko
|
||||
* use ttf files of googlefonts/opensans (https://github.com/maptiler/tileserver-gl/pull/1447) by @okimiko
|
||||
* Limit Elevation Lat/Long Output Length (https://github.com/maptiler/tileserver-gl/pull/1457) by @okimiko
|
||||
* Fetch style from url (https://github.com/maptiler/tileserver-gl/pull/1462) by @YoelRidgway
|
||||
* fix: memory leak on SIGHUP (https://github.com/maptiler/tileserver-gl/pull/1455) by @okimiko
|
||||
* fix: resolves Unimplemented type: 3 error for geojson format (https://github.com/maptiler/tileserver-gl/pull/1465) by @rjdjohnston
|
||||
* fix: Test light version in ct workflow - fix sqlite build in light (https://github.com/maptiler/tileserver-gl/pull/1477) by @acalcutt
|
||||
* fix: light version docker entrypoint permissions (https://github.com/maptiler/tileserver-gl/pull/1478) by @acalcutt
|
||||
|
||||
## 5.1.3
|
||||
* Fix SIGHUP (broken since 5.1.x) (https://github.com/maptiler/tileserver-gl/pull/1452) by @okimiko
|
||||
|
||||
## 5.1.2
|
||||
* Fix broken light (invalid use of heavy dependencies) (https://github.com/maptiler/tileserver-gl/pull/1449) by @okimiko
|
||||
|
||||
## 5.1.1
|
||||
* Fix wrong node version in Docker image (https://github.com/maptiler/tileserver-gl/pull/1438) by @acalcutt
|
||||
* Fix wrong node version in Docker image (https://github.com/maptiler/tileserver-gl/pull/1442) by @acalcutt
|
||||
|
||||
## 5.1.0
|
||||
* Update recommended node to v22 + Update docker images to use node 22 (https://github.com/maptiler/tileserver-gl/pull/1438) by @acalcutt
|
||||
|
|
90
Dockerfile
90
Dockerfile
|
@ -2,10 +2,11 @@ FROM ubuntu:jammy AS builder
|
|||
|
||||
ENV NODE_ENV="production"
|
||||
|
||||
RUN set -ex; \
|
||||
export DEBIAN_FRONTEND=noninteractive; \
|
||||
apt-get -qq update; \
|
||||
apt-get -y --no-install-recommends install \
|
||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||
|
||||
RUN export DEBIAN_FRONTEND=noninteractive && \
|
||||
apt-get update && \
|
||||
apt-get install -y --no-install-recommends --no-install-suggests \
|
||||
build-essential \
|
||||
ca-certificates \
|
||||
curl \
|
||||
|
@ -26,23 +27,17 @@ RUN set -ex; \
|
|||
librsvg2-common \
|
||||
libcurl4-openssl-dev \
|
||||
libpixman-1-dev \
|
||||
libpixman-1-0; \
|
||||
apt-get -y --purge autoremove; \
|
||||
apt-get clean; \
|
||||
rm -rf /var/lib/apt/lists/*;
|
||||
|
||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||
|
||||
RUN mkdir -p /etc/apt/keyrings; \
|
||||
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg; \
|
||||
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_22.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list; \
|
||||
apt-get -qq update; \
|
||||
apt-get install -y nodejs; \
|
||||
npm i -g npm@latest; \
|
||||
apt-get -y remove curl gnupg; \
|
||||
apt-get -y --purge autoremove; \
|
||||
apt-get clean; \
|
||||
rm -rf /var/lib/apt/lists/*;
|
||||
libpixman-1-0 && \
|
||||
mkdir -p /etc/apt/keyrings && \
|
||||
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \
|
||||
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_22.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list && \
|
||||
apt-get -qq update && \
|
||||
apt-get install -y --no-install-recommends --no-install-suggests nodejs && \
|
||||
npm i -g npm@latest && \
|
||||
apt-get -y remove curl gnupg && \
|
||||
apt-get -y --purge autoremove && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN mkdir -p /usr/src/app
|
||||
|
||||
|
@ -51,12 +46,12 @@ WORKDIR /usr/src/app
|
|||
COPY package.json /usr/src/app
|
||||
COPY package-lock.json /usr/src/app
|
||||
|
||||
RUN npm config set maxsockets 1; \
|
||||
npm config set fetch-retries 5; \
|
||||
npm config set fetch-retry-mintimeout 100000; \
|
||||
npm config set fetch-retry-maxtimeout 600000; \
|
||||
npm ci --omit=dev; \
|
||||
chown -R root:root /usr/src/app;
|
||||
RUN npm config set maxsockets 1 && \
|
||||
npm config set fetch-retries 5 && \
|
||||
npm config set fetch-retry-mintimeout 100000 && \
|
||||
npm config set fetch-retry-maxtimeout 600000 && \
|
||||
npm ci --omit=dev && \
|
||||
chown -R root:root /usr/src/app
|
||||
|
||||
FROM ubuntu:jammy AS final
|
||||
|
||||
|
@ -65,12 +60,13 @@ ENV \
|
|||
CHOKIDAR_USEPOLLING=1 \
|
||||
CHOKIDAR_INTERVAL=500
|
||||
|
||||
RUN set -ex; \
|
||||
export DEBIAN_FRONTEND=noninteractive; \
|
||||
groupadd -r node; \
|
||||
useradd -r -g node node; \
|
||||
apt-get -qq update; \
|
||||
apt-get -y --no-install-recommends install \
|
||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||
|
||||
RUN export DEBIAN_FRONTEND=noninteractive && \
|
||||
groupadd -r node && \
|
||||
useradd -r -g node node && \
|
||||
apt-get -qq update && \
|
||||
apt-get install -y --no-install-recommends --no-install-suggests \
|
||||
ca-certificates \
|
||||
curl \
|
||||
gnupg \
|
||||
|
@ -85,23 +81,17 @@ RUN set -ex; \
|
|||
libpixman-1-0 \
|
||||
libcurl4 \
|
||||
librsvg2-2 \
|
||||
libpango-1.0-0; \
|
||||
apt-get -y --purge autoremove; \
|
||||
apt-get clean; \
|
||||
rm -rf /var/lib/apt/lists/*;
|
||||
|
||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||
|
||||
RUN mkdir -p /etc/apt/keyrings; \
|
||||
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg; \
|
||||
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_22.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list; \
|
||||
apt-get -qq update; \
|
||||
apt-get install -y nodejs; \
|
||||
npm i -g npm@latest; \
|
||||
apt-get -y remove curl gnupg; \
|
||||
apt-get -y --purge autoremove; \
|
||||
apt-get clean; \
|
||||
rm -rf /var/lib/apt/lists/*;
|
||||
libpango-1.0-0 && \
|
||||
mkdir -p /etc/apt/keyrings && \
|
||||
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \
|
||||
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_22.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list && \
|
||||
apt-get -qq update && \
|
||||
apt-get install -y --no-install-recommends --no-install-suggests nodejs && \
|
||||
npm i -g npm@latest && \
|
||||
apt-get -y remove curl gnupg && \
|
||||
apt-get -y --purge autoremove && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY --from=builder /usr/src/app /usr/src/app
|
||||
|
||||
|
|
38
Dockerfile_build
Normal file
38
Dockerfile_build
Normal file
|
@ -0,0 +1,38 @@
|
|||
FROM ubuntu:jammy AS builder
|
||||
|
||||
ENV NODE_ENV="devel"
|
||||
|
||||
RUN export DEBIAN_FRONTEND=noninteractive && \
|
||||
apt-get update && \
|
||||
apt-get install -y --no-install-recommends --no-install-suggests \
|
||||
build-essential \
|
||||
ca-certificates \
|
||||
curl \
|
||||
gnupg \
|
||||
pkg-config \
|
||||
xvfb \
|
||||
libglfw3-dev \
|
||||
libuv1-dev \
|
||||
libjpeg-turbo8 \
|
||||
libicu70 \
|
||||
libcairo2-dev \
|
||||
libpango1.0-dev \
|
||||
libjpeg-dev \
|
||||
libgif-dev \
|
||||
librsvg2-dev \
|
||||
gir1.2-rsvg-2.0 \
|
||||
librsvg2-2 \
|
||||
librsvg2-common \
|
||||
libcurl4-openssl-dev \
|
||||
libpixman-1-dev \
|
||||
libpixman-1-0 && \
|
||||
mkdir -p /etc/apt/keyrings && \
|
||||
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \
|
||||
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_22.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list && \
|
||||
apt-get -qq update && \
|
||||
apt-get install -y --no-install-recommends --no-install-suggests nodejs && \
|
||||
npm i -g npm@latest && \
|
||||
apt-get -y remove curl gnupg && \
|
||||
apt-get -y --purge autoremove && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
|
@ -1,50 +1,81 @@
|
|||
FROM ubuntu:jammy
|
||||
FROM ubuntu:jammy AS builder
|
||||
|
||||
ENV NODE_ENV="production"
|
||||
|
||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||
|
||||
RUN export DEBIAN_FRONTEND=noninteractive && \
|
||||
apt-get update && \
|
||||
apt-get install -y --no-install-recommends --no-install-suggests \
|
||||
build-essential \
|
||||
ca-certificates \
|
||||
curl \
|
||||
gnupg && \
|
||||
mkdir -p /etc/apt/keyrings && \
|
||||
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \
|
||||
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_22.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list && \
|
||||
apt-get -qq update && \
|
||||
apt-get install -y --no-install-recommends --no-install-suggests nodejs && \
|
||||
npm i -g npm@latest && \
|
||||
apt-get -y remove curl gnupg && \
|
||||
apt-get -y --purge autoremove && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN mkdir -p /usr/src/app
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
COPY package.json /usr/src/app
|
||||
COPY package-lock.json /usr/src/app
|
||||
|
||||
RUN npm config set maxsockets 1 && \
|
||||
npm config set fetch-retries 5 && \
|
||||
npm config set fetch-retry-mintimeout 100000 && \
|
||||
npm config set fetch-retry-maxtimeout 600000 && \
|
||||
npm ci --omit=dev && \
|
||||
chown -R root:root /usr/src/app
|
||||
|
||||
FROM ubuntu:jammy AS final
|
||||
|
||||
ENV \
|
||||
NODE_ENV="production" \
|
||||
CHOKIDAR_USEPOLLING=1 \
|
||||
CHOKIDAR_INTERVAL=500
|
||||
|
||||
RUN set -ex; \
|
||||
export DEBIAN_FRONTEND=noninteractive; \
|
||||
groupadd -r node; \
|
||||
useradd -r -g node node; \
|
||||
apt-get -qq update; \
|
||||
apt-get -y --no-install-recommends install \
|
||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||
|
||||
RUN export DEBIAN_FRONTEND=noninteractive && \
|
||||
groupadd -r node && \
|
||||
useradd -r -g node node && \
|
||||
apt-get -qq update && \
|
||||
apt-get install -y --no-install-recommends --no-install-suggests \
|
||||
ca-certificates \
|
||||
curl \
|
||||
gnupg; \
|
||||
mkdir -p /etc/apt/keyrings; \
|
||||
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg; \
|
||||
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_22.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list; \
|
||||
apt-get -qq update; \
|
||||
apt-get install -y nodejs; \
|
||||
npm i -g npm@latest; \
|
||||
apt-get -y remove curl gnupg; \
|
||||
apt-get -y --purge autoremove; \
|
||||
apt-get clean; \
|
||||
rm -rf /var/lib/apt/lists/*;
|
||||
gnupg && \
|
||||
mkdir -p /etc/apt/keyrings && \
|
||||
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \
|
||||
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_22.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list && \
|
||||
apt-get -qq update && \
|
||||
apt-get install -y --no-install-recommends --no-install-suggests nodejs && \
|
||||
npm i -g npm@latest && \
|
||||
apt-get -y remove curl gnupg && \
|
||||
apt-get -y --purge autoremove && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
EXPOSE 8080
|
||||
COPY --from=builder /usr/src/app /usr/src/app
|
||||
|
||||
RUN mkdir -p /data; \
|
||||
chown node:node /data; \
|
||||
mkdir -p /usr/src/app;
|
||||
COPY . /usr/src/app
|
||||
|
||||
RUN mkdir -p /data && \
|
||||
chown node:node /data && \
|
||||
chmod +x /usr/src/app/docker-entrypoint.sh
|
||||
|
||||
VOLUME /data
|
||||
|
||||
WORKDIR /data
|
||||
|
||||
COPY / /usr/src/app
|
||||
|
||||
RUN cd /usr/src/app; \
|
||||
npm config set maxsockets 1; \
|
||||
npm config set fetch-retries 5; \
|
||||
npm config set fetch-retry-mintimeout 100000; \
|
||||
npm config set fetch-retry-maxtimeout 600000; \
|
||||
npm install --omit=dev; \
|
||||
chown -R root:root /usr/src/app; \
|
||||
chmod +x /usr/src/app/docker-entrypoint.sh;
|
||||
EXPOSE 8080
|
||||
|
||||
USER node:node
|
||||
|
||||
|
|
115
PUBLISHING.md
115
PUBLISHING.md
|
@ -1,19 +1,112 @@
|
|||
# Publishing new version
|
||||
# Publishing a New Version
|
||||
|
||||
1.) Change the version number in package.json. Run the following command in the package root directory, replacing <update_type> with one of the semantic versioning release types (prerelease, prepatch, preminor, premajor, patch, minor, major):
|
||||
npm version <update_type> --preid pre --no-git-tag-version
|
||||
This document outlines the process for publishing new versions of this project. We use a GitHub workflow for automated releases, but also provide manual steps for specific situations.
|
||||
|
||||
--preid specifies which suffix to use in the release such as pre, next, beta, rc, etc.
|
||||
## Automated Publishing via GitHub Workflow (Recommended)
|
||||
|
||||
prepatch, preminor, and premajor start a new series of pre-releases while bumping the patch, minor, or major version. E.g. premajor with --preid pre would do a prerelease for a new major using the -pre suffix (i.e. it would be a new major with -pre.0)
|
||||
This is the preferred method for publishing new versions. It automates the entire process, including version bumping, tagging, building, and publishing.
|
||||
|
||||
You can use prerelease to bump the version for a new pre-release version. E.g. you could run npm version prerelease --preid pre --no-git-tag-version to go from -pre.0 to -pre.1.
|
||||
1. **Prepare the Release:**
|
||||
|
||||
For regular versions, you can use patch, minor, or major. E.g. npm version major --no-git-tag-version.
|
||||
* **Choose the Version Increment:** Determine the appropriate semantic versioning increment (prerelease, prepatch, preminor, premajor, patch, minor, major).
|
||||
|
||||
2.) Update the changelog, which can be found in CHANGELOG.md. The heading must match ## <VERSION> exactly, or it will not be picked up. For example, for version 5.0.0:
|
||||
```## 5.0.0```
|
||||
* **Update `package.json`:** Use the `npm version` command to bump the version number. This command also supports creating pre-release versions using the `--preid pre` flag.
|
||||
|
||||
3.) Commit and push the changes.
|
||||
```bash
|
||||
# Example: Increment to a new patch version
|
||||
npm version patch --no-git-tag-version
|
||||
|
||||
4.) Run the 'Build, Test, Release' github workflow. The workflow will create a NPM, Docker, and Github release and Tag.
|
||||
# Example: Increment to a new minor version
|
||||
npm version minor --no-git-tag-version
|
||||
|
||||
# Example: Increment to a new major version
|
||||
npm version major --no-git-tag-version
|
||||
|
||||
# Example: Create a pre-release (e.g., -pre.0) version
|
||||
npm version prepatch --preid pre --no-git-tag-version
|
||||
# OR
|
||||
npm version preminor --preid pre --no-git-tag-version
|
||||
# OR
|
||||
npm version premajor --preid pre --no-git-tag-version
|
||||
|
||||
# Example: Increment an existing pre-release version (e.g., -pre.0 to -pre.1)
|
||||
npm version prerelease --preid pre --no-git-tag-version
|
||||
```
|
||||
|
||||
* `--no-git-tag-version`: This prevents `npm version` from automatically creating a git tag, as the GitHub workflow will handle this later.
|
||||
* `--preid pre`: Specifies that "pre" is the pre-release identifier.
|
||||
|
||||
* **Update the Changelog (`CHANGELOG.md`):** Add a new section for the release. The heading *must* exactly match the format `## <VERSION>`, where `<VERSION>` is the full version number from `package.json`. Describe the changes included in the release.
|
||||
|
||||
```markdown
|
||||
## 1.2.3
|
||||
* Added new feature X.
|
||||
```
|
||||
|
||||
2. **Commit and Push:** Commit the changes to `package.json`, `package-lock.json` and `CHANGELOG.md` to a the `master` branch. Create a pull request (PR) for review. If you are an administrator, you may push directly, but a PR is generally recommended for code review.
|
||||
|
||||
3. **Run the GitHub Workflow:** Once the PR is merged (or changes pushed directly), trigger the "Build, Test, Release" GitHub workflow. This workflow is responsible for:
|
||||
|
||||
* Building the project.
|
||||
* Running tests.
|
||||
* Creating an NPM package.
|
||||
* Building and pushing Docker images.
|
||||
* Creating a GitHub Release.
|
||||
* Creating a Git tag for the release.
|
||||
|
||||
## Manual Publishing (For Special Cases)
|
||||
|
||||
Use the following steps *only* when the automated workflow is not suitable (e.g., for debugging, specific environment needs).
|
||||
|
||||
1. **Update Version in `package.json`:** Modify the `version` field in `package.json` to the desired version number.
|
||||
|
||||
2. **Create and Push Git Tag:**
|
||||
|
||||
```bash
|
||||
git tag vX.X.X # Replace X.X.X with the version number
|
||||
git push origin --tags
|
||||
```
|
||||
|
||||
3. **NPM Publish Options (Choose ONE):**
|
||||
|
||||
* **Option A: Publish only the full `tileserver-gl` version:**
|
||||
|
||||
```bash
|
||||
npm publish --access public
|
||||
```
|
||||
|
||||
* **Option B: Build and Publish both `tileserver-gl` and `tileserver-gl-light` using `publish.js`:**
|
||||
|
||||
```bash
|
||||
# This script builds the light version and publishes both tileserver-gl and tileserver-gl-light to NPM.
|
||||
node publish.js
|
||||
```
|
||||
|
||||
* **Option C: Build only the `tileserver-gl-light` version (no publish):**
|
||||
|
||||
```bash
|
||||
# This script ONLY builds the light version (e.g., for local testing or Docker image creation) without publishing.
|
||||
node publish.js --no-publish
|
||||
```
|
||||
|
||||
4. **Build and Push Docker Images:**
|
||||
|
||||
```bash
|
||||
# Build the main image
|
||||
docker buildx build --platform linux/amd64 -t maptiler/tileserver-gl:latest -t maptiler/tileserver-gl:X.X.X . # Replace X.X.X
|
||||
docker push maptiler/tileserver-gl --all-tags
|
||||
|
||||
# Build the light image
|
||||
cd light
|
||||
docker buildx build --platform linux/amd64 -t maptiler/tileserver-gl-light:latest -t maptiler/tileserver-gl-light:X.X.X . # Replace X.X.X
|
||||
docker push maptiler/tileserver-gl-light --all-tags
|
||||
cd .. # Return to the project root
|
||||
```
|
||||
* Make sure you are logged into the docker registry before pushing. `docker login`
|
||||
|
||||
**Important Considerations for Manual Publishing:**
|
||||
|
||||
* **Consistency:** Ensure the version number in `package.json`, the Git tag, and the Docker image tags are identical.
|
||||
* **Credentials:** Verify that you have the necessary permissions to push Docker images and publish to NPM.
|
||||
* **Cleanliness:** Before building Docker images, ensure your working directory is clean to avoid including unwanted files in the image.
|
||||
* **Error Handling:** Manually publishing is more prone to errors. Double-check each step to avoid issues.
|
||||
|
|
27
README.md
27
README.md
|
@ -1,5 +1,32 @@
|
|||

|
||||
|
||||
# My TileServer GL
|
||||
|
||||
creare un folder dove mettere la mappa e i layers
|
||||
|
||||
Download vector tiles from [OpenMapTiles](https://data.maptiler.com/downloads/planet/).
|
||||
|
||||
scaricare i layers
|
||||
|
||||
wget https://github.com/maptiler/tileserver-gl/releases/download/v1.3.0/test_data.zip
|
||||
unzip test_data.zip
|
||||
|
||||
modificare nano config.json per inserire il nome del file .mbtiles (x es planetOSM.mbtiles )
|
||||
|
||||
far partire il docker
|
||||
|
||||
services:
|
||||
tileserver-gl:
|
||||
volumes:
|
||||
- /home/nvme/dockerdata/tileserver:/data
|
||||
ports:
|
||||
- 8115:8080
|
||||
image: maptiler/tileserver-gl:latest
|
||||
|
||||
oppure
|
||||
|
||||
sudo docker run -d -v /home/nvme/dockerdata/tileserver:/data -p 8115:8080 maptiler/tileserver-gl:latest
|
||||
|
||||
# TileServer GL
|
||||
[](https://github.com/maptiler/tileserver-gl/actions/workflows/pipeline.yml)
|
||||
[](https://hub.docker.com/r/maptiler/tileserver-gl/)
|
||||
|
|
|
@ -57,6 +57,9 @@ Example:
|
|||
"tilejson": {
|
||||
"format": "webp"
|
||||
}
|
||||
},
|
||||
"remote": {
|
||||
"style": "https://demotiles.maplibre.org/style.json"
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
|
@ -209,7 +212,7 @@ Not used by default.
|
|||
|
||||
Each item in this object defines one style (map). It can have the following options:
|
||||
|
||||
* ``style`` -- name of the style json file [required]
|
||||
* ``style`` -- name of the style json file or url of a remote hosted style [required]
|
||||
* ``serve_rendered`` -- whether to render the raster tiles for this style or not
|
||||
* ``serve_data`` -- whether to allow access to the original tiles, sprites and required glyphs
|
||||
* ``tilejson`` -- properties to add to the TileJSON created for the raster data
|
||||
|
|
|
@ -108,6 +108,8 @@ Source data
|
|||
|
||||
* the result will be a json object like ``{"z":7,"x":68,"y":45,"red":134,"green":66,"blue":0,"latitude":11.84069,"longitude":46.04798,"elevation":1602}``
|
||||
|
||||
* The elevation api is not available in the ``tileserver-gl-light`` version.
|
||||
|
||||
Static files
|
||||
===========
|
||||
* Static files are served at ``/files/{filename}``
|
||||
|
|
4938
package-lock.json
generated
4938
package-lock.json
generated
File diff suppressed because it is too large
Load diff
16
package.json
16
package.json
|
@ -1,11 +1,17 @@
|
|||
{
|
||||
"name": "tileserver-gl",
|
||||
"version": "5.1.1",
|
||||
"version": "5.2.0",
|
||||
"description": "Map tile server for JSON GL styles - vector and server side generated raster tiles",
|
||||
"main": "src/main.js",
|
||||
"bin": "src/main.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"prepare": "npm run copy:maplibre && npm run copy:maplibre-inspect && npm run copy:mapbox-rtl-text && npm run copy:leaflet && npm run copy:leaflet-hash",
|
||||
"copy:maplibre": "copyfiles -EVf node_modules/maplibre-gl/dist/maplibre-gl.js node_modules/maplibre-gl/dist/maplibre-gl.js.map node_modules/maplibre-gl/dist/maplibre-gl.css public/resources/",
|
||||
"copy:maplibre-inspect": "copyfiles -EVf node_modules/@maplibre/maplibre-gl-inspect/dist/maplibre-gl-inspect.js node_modules/@maplibre/maplibre-gl-inspect/dist/maplibre-gl-inspect.js.map node_modules/@maplibre/maplibre-gl-inspect/dist/maplibre-gl-inspect.css public/resources/",
|
||||
"copy:mapbox-rtl-text": "copyfiles -EVf node_modules/@mapbox/mapbox-gl-rtl-text/dist/mapbox-gl-rtl-text.js public/resources/",
|
||||
"copy:leaflet": "copyfiles -EVf node_modules/leaflet/dist/leaflet.js node_modules/leaflet/dist/leaflet.js.map node_modules/leaflet/dist/leaflet.css node_modules/leaflet/dist/leaflet-hash.js public/resources/",
|
||||
"copy:leaflet-hash": "copyfiles -EVf node_modules/leaflet-hash/leaflet-hash.js public/resources/",
|
||||
"test": "mocha test/**.js --timeout 10000 --exit",
|
||||
"test-docker": "xvfb-run npm test",
|
||||
"lint:yml": "yamllint --schema=CORE_SCHEMA *.{yml,yaml}",
|
||||
|
@ -19,24 +25,30 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@jsse/pbfont": "^0.2.2",
|
||||
"@mapbox/mapbox-gl-rtl-text": "0.3.0",
|
||||
"@mapbox/mbtiles": "0.12.1",
|
||||
"@mapbox/polyline": "^1.2.1",
|
||||
"@mapbox/sphericalmercator": "1.2.0",
|
||||
"@mapbox/vector-tile": "2.0.3",
|
||||
"@maplibre/maplibre-gl-inspect": "1.7.0",
|
||||
"@maplibre/maplibre-gl-native": "6.0.0",
|
||||
"@maplibre/maplibre-gl-style-spec": "20.3.1",
|
||||
"@sindresorhus/fnv1a": "3.1.0",
|
||||
"advanced-pool": "0.3.3",
|
||||
"axios": "^1.7.7",
|
||||
"axios": "^1.8.2",
|
||||
"canvas": "3.0.1",
|
||||
"chokidar": "3.6.0",
|
||||
"clone": "2.1.2",
|
||||
"color": "4.2.3",
|
||||
"commander": "12.1.0",
|
||||
"copyfiles": "2.4.1",
|
||||
"cors": "2.8.5",
|
||||
"express": "5.0.1",
|
||||
"handlebars": "4.7.8",
|
||||
"http-shutdown": "1.2.2",
|
||||
"leaflet": "1.9.4",
|
||||
"leaflet-hash": "0.2.1",
|
||||
"maplibre-gl": "4.7.1",
|
||||
"morgan": "1.10.0",
|
||||
"pbf": "4.0.1",
|
||||
"pmtiles": "3.0.7",
|
||||
|
|
|
@ -18,7 +18,7 @@ class ElevationInfoControl {
|
|||
|
||||
map.on('click', (e) => {
|
||||
var url = this.url;
|
||||
var coord = {"z": Math.floor(map.getZoom()), "x": e.lngLat["lng"], "y": e.lngLat["lat"]};
|
||||
var coord = {"z": Math.floor(map.getZoom()), "x": e.lngLat["lng"].toFixed(7), "y": e.lngLat["lat"].toFixed(7)};
|
||||
for(var key in coord) {
|
||||
url = url.replace(new RegExp('{'+ key +'}','g'), coord[key]);
|
||||
}
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,162 +0,0 @@
|
|||
(function(window) {
|
||||
var HAS_HASHCHANGE = (function() {
|
||||
var doc_mode = window.documentMode;
|
||||
return ('onhashchange' in window) &&
|
||||
(doc_mode === undefined || doc_mode > 7);
|
||||
})();
|
||||
|
||||
L.Hash = function(map) {
|
||||
this.onHashChange = L.Util.bind(this.onHashChange, this);
|
||||
|
||||
if (map) {
|
||||
this.init(map);
|
||||
}
|
||||
};
|
||||
|
||||
L.Hash.parseHash = function(hash) {
|
||||
if(hash.indexOf('#') === 0) {
|
||||
hash = hash.substr(1);
|
||||
}
|
||||
var args = hash.split("/");
|
||||
if (args.length == 3) {
|
||||
var zoom = parseInt(args[0], 10),
|
||||
lat = parseFloat(args[1]),
|
||||
lon = parseFloat(args[2]);
|
||||
if (isNaN(zoom) || isNaN(lat) || isNaN(lon)) {
|
||||
return false;
|
||||
} else {
|
||||
return {
|
||||
center: new L.LatLng(lat, lon),
|
||||
zoom: zoom
|
||||
};
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
L.Hash.formatHash = function(map) {
|
||||
var center = map.getCenter(),
|
||||
zoom = map.getZoom(),
|
||||
precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
|
||||
|
||||
return "#" + [zoom,
|
||||
center.lat.toFixed(precision),
|
||||
center.lng.toFixed(precision)
|
||||
].join("/");
|
||||
},
|
||||
|
||||
L.Hash.prototype = {
|
||||
map: null,
|
||||
lastHash: null,
|
||||
|
||||
parseHash: L.Hash.parseHash,
|
||||
formatHash: L.Hash.formatHash,
|
||||
|
||||
init: function(map) {
|
||||
this.map = map;
|
||||
|
||||
// reset the hash
|
||||
this.lastHash = null;
|
||||
this.onHashChange();
|
||||
|
||||
if (!this.isListening) {
|
||||
this.startListening();
|
||||
}
|
||||
},
|
||||
|
||||
removeFrom: function(map) {
|
||||
if (this.changeTimeout) {
|
||||
clearTimeout(this.changeTimeout);
|
||||
}
|
||||
|
||||
if (this.isListening) {
|
||||
this.stopListening();
|
||||
}
|
||||
|
||||
this.map = null;
|
||||
},
|
||||
|
||||
onMapMove: function() {
|
||||
// bail if we're moving the map (updating from a hash),
|
||||
// or if the map is not yet loaded
|
||||
|
||||
if (this.movingMap || !this.map._loaded) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var hash = this.formatHash(this.map);
|
||||
if (this.lastHash != hash) {
|
||||
location.replace(hash);
|
||||
this.lastHash = hash;
|
||||
}
|
||||
},
|
||||
|
||||
movingMap: false,
|
||||
update: function() {
|
||||
var hash = location.hash;
|
||||
if (hash === this.lastHash) {
|
||||
return;
|
||||
}
|
||||
var parsed = this.parseHash(hash);
|
||||
if (parsed) {
|
||||
this.movingMap = true;
|
||||
|
||||
this.map.setView(parsed.center, parsed.zoom);
|
||||
|
||||
this.movingMap = false;
|
||||
} else {
|
||||
this.onMapMove(this.map);
|
||||
}
|
||||
},
|
||||
|
||||
// defer hash change updates every 100ms
|
||||
changeDefer: 100,
|
||||
changeTimeout: null,
|
||||
onHashChange: function() {
|
||||
// throttle calls to update() so that they only happen every
|
||||
// `changeDefer` ms
|
||||
if (!this.changeTimeout) {
|
||||
var that = this;
|
||||
this.changeTimeout = setTimeout(function() {
|
||||
that.update();
|
||||
that.changeTimeout = null;
|
||||
}, this.changeDefer);
|
||||
}
|
||||
},
|
||||
|
||||
isListening: false,
|
||||
hashChangeInterval: null,
|
||||
startListening: function() {
|
||||
this.map.on("moveend", this.onMapMove, this);
|
||||
|
||||
if (HAS_HASHCHANGE) {
|
||||
L.DomEvent.addListener(window, "hashchange", this.onHashChange);
|
||||
} else {
|
||||
clearInterval(this.hashChangeInterval);
|
||||
this.hashChangeInterval = setInterval(this.onHashChange, 50);
|
||||
}
|
||||
this.isListening = true;
|
||||
},
|
||||
|
||||
stopListening: function() {
|
||||
this.map.off("moveend", this.onMapMove, this);
|
||||
|
||||
if (HAS_HASHCHANGE) {
|
||||
L.DomEvent.removeListener(window, "hashchange", this.onHashChange);
|
||||
} else {
|
||||
clearInterval(this.hashChangeInterval);
|
||||
}
|
||||
this.isListening = false;
|
||||
}
|
||||
};
|
||||
L.hash = function(map) {
|
||||
return new L.Hash(map);
|
||||
};
|
||||
L.Map.prototype.addHash = function() {
|
||||
this._hash = L.hash(this);
|
||||
};
|
||||
L.Map.prototype.removeHash = function() {
|
||||
this._hash.removeFrom();
|
||||
};
|
||||
})(window);
|
|
@ -1,661 +0,0 @@
|
|||
/* required styles */
|
||||
|
||||
.leaflet-pane,
|
||||
.leaflet-tile,
|
||||
.leaflet-marker-icon,
|
||||
.leaflet-marker-shadow,
|
||||
.leaflet-tile-container,
|
||||
.leaflet-pane > svg,
|
||||
.leaflet-pane > canvas,
|
||||
.leaflet-zoom-box,
|
||||
.leaflet-image-layer,
|
||||
.leaflet-layer {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
.leaflet-container {
|
||||
overflow: hidden;
|
||||
}
|
||||
.leaflet-tile,
|
||||
.leaflet-marker-icon,
|
||||
.leaflet-marker-shadow {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-user-drag: none;
|
||||
}
|
||||
/* Prevents IE11 from highlighting tiles in blue */
|
||||
.leaflet-tile::selection {
|
||||
background: transparent;
|
||||
}
|
||||
/* Safari renders non-retina tile on retina better with this, but Chrome is worse */
|
||||
.leaflet-safari .leaflet-tile {
|
||||
image-rendering: -webkit-optimize-contrast;
|
||||
}
|
||||
/* hack that prevents hw layers "stretching" when loading new tiles */
|
||||
.leaflet-safari .leaflet-tile-container {
|
||||
width: 1600px;
|
||||
height: 1600px;
|
||||
-webkit-transform-origin: 0 0;
|
||||
}
|
||||
.leaflet-marker-icon,
|
||||
.leaflet-marker-shadow {
|
||||
display: block;
|
||||
}
|
||||
/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */
|
||||
/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */
|
||||
.leaflet-container .leaflet-overlay-pane svg {
|
||||
max-width: none !important;
|
||||
max-height: none !important;
|
||||
}
|
||||
.leaflet-container .leaflet-marker-pane img,
|
||||
.leaflet-container .leaflet-shadow-pane img,
|
||||
.leaflet-container .leaflet-tile-pane img,
|
||||
.leaflet-container img.leaflet-image-layer,
|
||||
.leaflet-container .leaflet-tile {
|
||||
max-width: none !important;
|
||||
max-height: none !important;
|
||||
width: auto;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.leaflet-container img.leaflet-tile {
|
||||
/* See: https://bugs.chromium.org/p/chromium/issues/detail?id=600120 */
|
||||
mix-blend-mode: plus-lighter;
|
||||
}
|
||||
|
||||
.leaflet-container.leaflet-touch-zoom {
|
||||
-ms-touch-action: pan-x pan-y;
|
||||
touch-action: pan-x pan-y;
|
||||
}
|
||||
.leaflet-container.leaflet-touch-drag {
|
||||
-ms-touch-action: pinch-zoom;
|
||||
/* Fallback for FF which doesn't support pinch-zoom */
|
||||
touch-action: none;
|
||||
touch-action: pinch-zoom;
|
||||
}
|
||||
.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom {
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
.leaflet-container {
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
.leaflet-container a {
|
||||
-webkit-tap-highlight-color: rgba(51, 181, 229, 0.4);
|
||||
}
|
||||
.leaflet-tile {
|
||||
filter: inherit;
|
||||
visibility: hidden;
|
||||
}
|
||||
.leaflet-tile-loaded {
|
||||
visibility: inherit;
|
||||
}
|
||||
.leaflet-zoom-box {
|
||||
width: 0;
|
||||
height: 0;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
z-index: 800;
|
||||
}
|
||||
/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */
|
||||
.leaflet-overlay-pane svg {
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
.leaflet-pane { z-index: 400; }
|
||||
|
||||
.leaflet-tile-pane { z-index: 200; }
|
||||
.leaflet-overlay-pane { z-index: 400; }
|
||||
.leaflet-shadow-pane { z-index: 500; }
|
||||
.leaflet-marker-pane { z-index: 600; }
|
||||
.leaflet-tooltip-pane { z-index: 650; }
|
||||
.leaflet-popup-pane { z-index: 700; }
|
||||
|
||||
.leaflet-map-pane canvas { z-index: 100; }
|
||||
.leaflet-map-pane svg { z-index: 200; }
|
||||
|
||||
.leaflet-vml-shape {
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
}
|
||||
.lvml {
|
||||
behavior: url(#default#VML);
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
|
||||
/* control positioning */
|
||||
|
||||
.leaflet-control {
|
||||
position: relative;
|
||||
z-index: 800;
|
||||
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
|
||||
pointer-events: auto;
|
||||
}
|
||||
.leaflet-top,
|
||||
.leaflet-bottom {
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
pointer-events: none;
|
||||
}
|
||||
.leaflet-top {
|
||||
top: 0;
|
||||
}
|
||||
.leaflet-right {
|
||||
right: 0;
|
||||
}
|
||||
.leaflet-bottom {
|
||||
bottom: 0;
|
||||
}
|
||||
.leaflet-left {
|
||||
left: 0;
|
||||
}
|
||||
.leaflet-control {
|
||||
float: left;
|
||||
clear: both;
|
||||
}
|
||||
.leaflet-right .leaflet-control {
|
||||
float: right;
|
||||
}
|
||||
.leaflet-top .leaflet-control {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.leaflet-bottom .leaflet-control {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.leaflet-left .leaflet-control {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.leaflet-right .leaflet-control {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
|
||||
/* zoom and fade animations */
|
||||
|
||||
.leaflet-fade-anim .leaflet-popup {
|
||||
opacity: 0;
|
||||
-webkit-transition: opacity 0.2s linear;
|
||||
-moz-transition: opacity 0.2s linear;
|
||||
transition: opacity 0.2s linear;
|
||||
}
|
||||
.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
|
||||
opacity: 1;
|
||||
}
|
||||
.leaflet-zoom-animated {
|
||||
-webkit-transform-origin: 0 0;
|
||||
-ms-transform-origin: 0 0;
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
svg.leaflet-zoom-animated {
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.leaflet-zoom-anim .leaflet-zoom-animated {
|
||||
-webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);
|
||||
-moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1);
|
||||
transition: transform 0.25s cubic-bezier(0,0,0.25,1);
|
||||
}
|
||||
.leaflet-zoom-anim .leaflet-tile,
|
||||
.leaflet-pan-anim .leaflet-tile {
|
||||
-webkit-transition: none;
|
||||
-moz-transition: none;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.leaflet-zoom-anim .leaflet-zoom-hide {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
|
||||
/* cursors */
|
||||
|
||||
.leaflet-interactive {
|
||||
cursor: pointer;
|
||||
}
|
||||
.leaflet-grab {
|
||||
cursor: -webkit-grab;
|
||||
cursor: -moz-grab;
|
||||
cursor: grab;
|
||||
}
|
||||
.leaflet-crosshair,
|
||||
.leaflet-crosshair .leaflet-interactive {
|
||||
cursor: crosshair;
|
||||
}
|
||||
.leaflet-popup-pane,
|
||||
.leaflet-control {
|
||||
cursor: auto;
|
||||
}
|
||||
.leaflet-dragging .leaflet-grab,
|
||||
.leaflet-dragging .leaflet-grab .leaflet-interactive,
|
||||
.leaflet-dragging .leaflet-marker-draggable {
|
||||
cursor: move;
|
||||
cursor: -webkit-grabbing;
|
||||
cursor: -moz-grabbing;
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
/* marker & overlays interactivity */
|
||||
.leaflet-marker-icon,
|
||||
.leaflet-marker-shadow,
|
||||
.leaflet-image-layer,
|
||||
.leaflet-pane > svg path,
|
||||
.leaflet-tile-container {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.leaflet-marker-icon.leaflet-interactive,
|
||||
.leaflet-image-layer.leaflet-interactive,
|
||||
.leaflet-pane > svg path.leaflet-interactive,
|
||||
svg.leaflet-image-layer.leaflet-interactive path {
|
||||
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
/* visual tweaks */
|
||||
|
||||
.leaflet-container {
|
||||
background: #ddd;
|
||||
outline-offset: 1px;
|
||||
}
|
||||
.leaflet-container a {
|
||||
color: #0078A8;
|
||||
}
|
||||
.leaflet-zoom-box {
|
||||
border: 2px dotted #38f;
|
||||
background: rgba(255,255,255,0.5);
|
||||
}
|
||||
|
||||
|
||||
/* general typography */
|
||||
.leaflet-container {
|
||||
font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
|
||||
font-size: 12px;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
|
||||
/* general toolbar styles */
|
||||
|
||||
.leaflet-bar {
|
||||
box-shadow: 0 1px 5px rgba(0,0,0,0.65);
|
||||
border-radius: 4px;
|
||||
}
|
||||
.leaflet-bar a {
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid #ccc;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
display: block;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
}
|
||||
.leaflet-bar a,
|
||||
.leaflet-control-layers-toggle {
|
||||
background-position: 50% 50%;
|
||||
background-repeat: no-repeat;
|
||||
display: block;
|
||||
}
|
||||
.leaflet-bar a:hover,
|
||||
.leaflet-bar a:focus {
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
.leaflet-bar a:first-child {
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
}
|
||||
.leaflet-bar a:last-child {
|
||||
border-bottom-left-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
border-bottom: none;
|
||||
}
|
||||
.leaflet-bar a.leaflet-disabled {
|
||||
cursor: default;
|
||||
background-color: #f4f4f4;
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.leaflet-touch .leaflet-bar a {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
.leaflet-touch .leaflet-bar a:first-child {
|
||||
border-top-left-radius: 2px;
|
||||
border-top-right-radius: 2px;
|
||||
}
|
||||
.leaflet-touch .leaflet-bar a:last-child {
|
||||
border-bottom-left-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
}
|
||||
|
||||
/* zoom control */
|
||||
|
||||
.leaflet-control-zoom-in,
|
||||
.leaflet-control-zoom-out {
|
||||
font: bold 18px 'Lucida Console', Monaco, monospace;
|
||||
text-indent: 1px;
|
||||
}
|
||||
|
||||
.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
|
||||
/* layers control */
|
||||
|
||||
.leaflet-control-layers {
|
||||
box-shadow: 0 1px 5px rgba(0,0,0,0.4);
|
||||
background: #fff;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.leaflet-control-layers-toggle {
|
||||
background-image: url(images/layers.png);
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
.leaflet-retina .leaflet-control-layers-toggle {
|
||||
background-image: url(images/layers-2x.png);
|
||||
background-size: 26px 26px;
|
||||
}
|
||||
.leaflet-touch .leaflet-control-layers-toggle {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
}
|
||||
.leaflet-control-layers .leaflet-control-layers-list,
|
||||
.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
|
||||
display: none;
|
||||
}
|
||||
.leaflet-control-layers-expanded .leaflet-control-layers-list {
|
||||
display: block;
|
||||
position: relative;
|
||||
}
|
||||
.leaflet-control-layers-expanded {
|
||||
padding: 6px 10px 6px 6px;
|
||||
color: #333;
|
||||
background: #fff;
|
||||
}
|
||||
.leaflet-control-layers-scrollbar {
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
padding-right: 5px;
|
||||
}
|
||||
.leaflet-control-layers-selector {
|
||||
margin-top: 2px;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
.leaflet-control-layers label {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
font-size: 1.08333em;
|
||||
}
|
||||
.leaflet-control-layers-separator {
|
||||
height: 0;
|
||||
border-top: 1px solid #ddd;
|
||||
margin: 5px -10px 5px -6px;
|
||||
}
|
||||
|
||||
/* Default icon URLs */
|
||||
.leaflet-default-icon-path { /* used only in path-guessing heuristic, see L.Icon.Default */
|
||||
background-image: url(images/marker-icon.png);
|
||||
}
|
||||
|
||||
|
||||
/* attribution and scale controls */
|
||||
|
||||
.leaflet-container .leaflet-control-attribution {
|
||||
background: #fff;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
margin: 0;
|
||||
}
|
||||
.leaflet-control-attribution,
|
||||
.leaflet-control-scale-line {
|
||||
padding: 0 5px;
|
||||
color: #333;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.leaflet-control-attribution a {
|
||||
text-decoration: none;
|
||||
}
|
||||
.leaflet-control-attribution a:hover,
|
||||
.leaflet-control-attribution a:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.leaflet-attribution-flag {
|
||||
display: inline !important;
|
||||
vertical-align: baseline !important;
|
||||
width: 1em;
|
||||
height: 0.6669em;
|
||||
}
|
||||
.leaflet-left .leaflet-control-scale {
|
||||
margin-left: 5px;
|
||||
}
|
||||
.leaflet-bottom .leaflet-control-scale {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.leaflet-control-scale-line {
|
||||
border: 2px solid #777;
|
||||
border-top: none;
|
||||
line-height: 1.1;
|
||||
padding: 2px 5px 1px;
|
||||
white-space: nowrap;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
text-shadow: 1px 1px #fff;
|
||||
}
|
||||
.leaflet-control-scale-line:not(:first-child) {
|
||||
border-top: 2px solid #777;
|
||||
border-bottom: none;
|
||||
margin-top: -2px;
|
||||
}
|
||||
.leaflet-control-scale-line:not(:first-child):not(:last-child) {
|
||||
border-bottom: 2px solid #777;
|
||||
}
|
||||
|
||||
.leaflet-touch .leaflet-control-attribution,
|
||||
.leaflet-touch .leaflet-control-layers,
|
||||
.leaflet-touch .leaflet-bar {
|
||||
box-shadow: none;
|
||||
}
|
||||
.leaflet-touch .leaflet-control-layers,
|
||||
.leaflet-touch .leaflet-bar {
|
||||
border: 2px solid rgba(0,0,0,0.2);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
|
||||
/* popup */
|
||||
|
||||
.leaflet-popup {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.leaflet-popup-content-wrapper {
|
||||
padding: 1px;
|
||||
text-align: left;
|
||||
border-radius: 12px;
|
||||
}
|
||||
.leaflet-popup-content {
|
||||
margin: 13px 24px 13px 20px;
|
||||
line-height: 1.3;
|
||||
font-size: 13px;
|
||||
font-size: 1.08333em;
|
||||
min-height: 1px;
|
||||
}
|
||||
.leaflet-popup-content p {
|
||||
margin: 17px 0;
|
||||
margin: 1.3em 0;
|
||||
}
|
||||
.leaflet-popup-tip-container {
|
||||
width: 40px;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
margin-top: -1px;
|
||||
margin-left: -20px;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
.leaflet-popup-tip {
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
padding: 1px;
|
||||
|
||||
margin: -10px auto 0;
|
||||
pointer-events: auto;
|
||||
|
||||
-webkit-transform: rotate(45deg);
|
||||
-moz-transform: rotate(45deg);
|
||||
-ms-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
.leaflet-popup-content-wrapper,
|
||||
.leaflet-popup-tip {
|
||||
background: white;
|
||||
color: #333;
|
||||
box-shadow: 0 3px 14px rgba(0,0,0,0.4);
|
||||
}
|
||||
.leaflet-container a.leaflet-popup-close-button {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
border: none;
|
||||
text-align: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
font: 16px/24px Tahoma, Verdana, sans-serif;
|
||||
color: #757575;
|
||||
text-decoration: none;
|
||||
background: transparent;
|
||||
}
|
||||
.leaflet-container a.leaflet-popup-close-button:hover,
|
||||
.leaflet-container a.leaflet-popup-close-button:focus {
|
||||
color: #585858;
|
||||
}
|
||||
.leaflet-popup-scrolled {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.leaflet-oldie .leaflet-popup-content-wrapper {
|
||||
-ms-zoom: 1;
|
||||
}
|
||||
.leaflet-oldie .leaflet-popup-tip {
|
||||
width: 24px;
|
||||
margin: 0 auto;
|
||||
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
|
||||
filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
|
||||
}
|
||||
|
||||
.leaflet-oldie .leaflet-control-zoom,
|
||||
.leaflet-oldie .leaflet-control-layers,
|
||||
.leaflet-oldie .leaflet-popup-content-wrapper,
|
||||
.leaflet-oldie .leaflet-popup-tip {
|
||||
border: 1px solid #999;
|
||||
}
|
||||
|
||||
|
||||
/* div icon */
|
||||
|
||||
.leaflet-div-icon {
|
||||
background: #fff;
|
||||
border: 1px solid #666;
|
||||
}
|
||||
|
||||
|
||||
/* Tooltip */
|
||||
/* Base styles for the element that has a tooltip */
|
||||
.leaflet-tooltip {
|
||||
position: absolute;
|
||||
padding: 6px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #fff;
|
||||
border-radius: 3px;
|
||||
color: #222;
|
||||
white-space: nowrap;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.4);
|
||||
}
|
||||
.leaflet-tooltip.leaflet-interactive {
|
||||
cursor: pointer;
|
||||
pointer-events: auto;
|
||||
}
|
||||
.leaflet-tooltip-top:before,
|
||||
.leaflet-tooltip-bottom:before,
|
||||
.leaflet-tooltip-left:before,
|
||||
.leaflet-tooltip-right:before {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
border: 6px solid transparent;
|
||||
background: transparent;
|
||||
content: "";
|
||||
}
|
||||
|
||||
/* Directions */
|
||||
|
||||
.leaflet-tooltip-bottom {
|
||||
margin-top: 6px;
|
||||
}
|
||||
.leaflet-tooltip-top {
|
||||
margin-top: -6px;
|
||||
}
|
||||
.leaflet-tooltip-bottom:before,
|
||||
.leaflet-tooltip-top:before {
|
||||
left: 50%;
|
||||
margin-left: -6px;
|
||||
}
|
||||
.leaflet-tooltip-top:before {
|
||||
bottom: 0;
|
||||
margin-bottom: -12px;
|
||||
border-top-color: #fff;
|
||||
}
|
||||
.leaflet-tooltip-bottom:before {
|
||||
top: 0;
|
||||
margin-top: -12px;
|
||||
margin-left: -6px;
|
||||
border-bottom-color: #fff;
|
||||
}
|
||||
.leaflet-tooltip-left {
|
||||
margin-left: -6px;
|
||||
}
|
||||
.leaflet-tooltip-right {
|
||||
margin-left: 6px;
|
||||
}
|
||||
.leaflet-tooltip-left:before,
|
||||
.leaflet-tooltip-right:before {
|
||||
top: 50%;
|
||||
margin-top: -6px;
|
||||
}
|
||||
.leaflet-tooltip-left:before {
|
||||
right: 0;
|
||||
margin-right: -12px;
|
||||
border-left-color: #fff;
|
||||
}
|
||||
.leaflet-tooltip-right:before {
|
||||
left: 0;
|
||||
margin-left: -12px;
|
||||
border-right-color: #fff;
|
||||
}
|
||||
|
||||
/* Printing */
|
||||
|
||||
@media print {
|
||||
/* Prevent printers from removing background-images of controls. */
|
||||
.leaflet-control {
|
||||
-webkit-print-color-adjust: exact;
|
||||
print-color-adjust: exact;
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,40 +0,0 @@
|
|||
.maplibregl-inspect_popup {
|
||||
color: #333;
|
||||
display: table;
|
||||
}
|
||||
|
||||
.maplibregl-inspect_feature:not(:last-child) {
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.maplibregl-inspect_layer:before {
|
||||
content: '#';
|
||||
}
|
||||
|
||||
.maplibregl-inspect_layer {
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.maplibregl-inspect_property {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
.maplibregl-inspect_property-value {
|
||||
display: table-cell;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.maplibregl-inspect_property-name {
|
||||
display: table-cell;
|
||||
padding-right: 10px;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.maplibregl-ctrl-inspect {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23333333' preserveAspectRatio='xMidYMid meet' viewBox='-10 -10 60 60'%3E%3Cg%3E%3Cpath d='m15 21.6q0-2 1.5-3.5t3.5-1.5 3.5 1.5 1.5 3.5-1.5 3.6-3.5 1.4-3.5-1.4-1.5-3.6z m18.4 11.1l-6.4-6.5q1.4-2.1 1.4-4.6 0-3.4-2.5-5.8t-5.9-2.4-5.9 2.4-2.5 5.8 2.5 5.9 5.9 2.5q2.4 0 4.6-1.4l7.4 7.4q-0.9 0.6-2 0.6h-20q-1.3 0-2.3-0.9t-1.1-2.3l0.1-26.8q0-1.3 1-2.3t2.3-0.9h13.4l10 10v19.3z'%3E%3C/path%3E%3C/g%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.maplibregl-ctrl-map {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23333333' viewBox='-10 -10 60 60' preserveAspectRatio='xMidYMid meet'%3E%3Cg%3E%3Cpath d='m25 31.640000000000004v-19.766666666666673l-10-3.511666666666663v19.766666666666666z m9.140000000000008-26.640000000000004q0.8599999999999923 0 0.8599999999999923 0.8600000000000003v25.156666666666666q0 0.625-0.625 0.783333333333335l-9.375 3.1999999999999993-10-3.5133333333333354-8.906666666666668 3.4383333333333326-0.2333333333333334 0.07833333333333314q-0.8616666666666664 0-0.8616666666666664-0.8599999999999994v-25.156666666666663q0-0.625 0.6233333333333331-0.7833333333333332l9.378333333333334-3.198333333333334 10 3.5133333333333336 8.905000000000001-3.4383333333333344z'%3E%3C/path%3E%3C/g%3E%3C/svg%3E");
|
||||
}
|
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -9,7 +9,9 @@
|
|||
<link rel="stylesheet" type="text/css" href="{{public_url}}maplibre-gl-inspect.css{{&key_query}}" />
|
||||
<script src="{{public_url}}maplibre-gl.js{{&key_query}}"></script>
|
||||
<script src="{{public_url}}maplibre-gl-inspect.js{{&key_query}}"></script>
|
||||
{{^is_light}}
|
||||
<script src="{{public_url}}elevation-control.js{{&key_query}}"></script>
|
||||
{{/is_light}}
|
||||
<style>
|
||||
body {background:#fff;color:#333;font-family:Arial, sans-serif;}
|
||||
{{^is_terrain}}
|
||||
|
@ -21,7 +23,9 @@
|
|||
h1 {position:absolute;top:5px;right:0;width:240px;margin:0;line-height:20px;font-size:20px;}
|
||||
#layerList {position:absolute;top:35px;right:0;bottom:0;width:240px;overflow:auto;}
|
||||
#layerList div div {width:15px;height:15px;display:inline-block;}
|
||||
{{^is_light}}
|
||||
.maplibre-ctrl-elevation { padding-left: 5px; padding-right: 5px; }
|
||||
{{/is_light}}
|
||||
</style>
|
||||
{{/use_maplibre}}
|
||||
{{^use_maplibre}}
|
||||
|
@ -135,11 +139,13 @@
|
|||
})
|
||||
);
|
||||
|
||||
{{^is_light}}
|
||||
map.addControl(
|
||||
new ElevationInfoControl({
|
||||
url: "{{public_url}}data/{{id}}/elevation/{z}/{x}/{y}"
|
||||
})
|
||||
);
|
||||
{{/is_light}}
|
||||
{{/is_terrain}}
|
||||
{{^is_terrain}}
|
||||
|
||||
|
|
|
@ -124,9 +124,9 @@
|
|||
{{/is_vector}}
|
||||
{{^is_vector}}
|
||||
<a class="btn" href="{{public_url}}data/{{@key}}/{{&../key_query}}{{viewer_hash}}">View</a>
|
||||
{{#elevation_link}}
|
||||
{{#is_terrain}}
|
||||
<a class="btn" href="{{public_url}}data/preview/{{@key}}/{{&../key_query}}{{viewer_hash}}">Preview Terrain</a>
|
||||
{{/elevation_link}}
|
||||
{{/is_terrain}}
|
||||
{{/is_vector}}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -8,8 +8,6 @@ import express from 'express';
|
|||
import Pbf from 'pbf';
|
||||
import { VectorTile } from '@mapbox/vector-tile';
|
||||
import SphericalMercator from '@mapbox/sphericalmercator';
|
||||
import { Image, createCanvas } from 'canvas';
|
||||
import sharp from 'sharp';
|
||||
|
||||
import {
|
||||
fixTileJSONCenter,
|
||||
|
@ -21,6 +19,21 @@ import { getPMtilesInfo, openPMtiles } from './pmtiles_adapter.js';
|
|||
import { gunzipP, gzipP } from './promises.js';
|
||||
import { openMbTilesWrapper } from './mbtiles_wrapper.js';
|
||||
|
||||
import fs from 'node:fs';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const packageJson = JSON.parse(
|
||||
fs.readFileSync(
|
||||
path.dirname(fileURLToPath(import.meta.url)) + '/../package.json',
|
||||
'utf8',
|
||||
),
|
||||
);
|
||||
|
||||
const isLight = packageJson.name.slice(-6) === '-light';
|
||||
const serve_rendered = (
|
||||
await import(`${!isLight ? `./serve_rendered.js` : `./serve_light.js`}`)
|
||||
).serve_rendered;
|
||||
|
||||
export const serve_data = {
|
||||
/**
|
||||
* Initializes the serve_data module.
|
||||
|
@ -101,12 +114,13 @@ export const serve_data = {
|
|||
let headers = fetchTile.headers;
|
||||
let isGzipped = data.slice(0, 2).indexOf(Buffer.from([0x1f, 0x8b])) === 0;
|
||||
|
||||
if (tileJSONFormat === 'pbf') {
|
||||
if (options.dataDecoratorFunc) {
|
||||
if (isGzipped) {
|
||||
data = await gunzipP(data);
|
||||
isGzipped = false;
|
||||
}
|
||||
|
||||
if (tileJSONFormat === 'pbf') {
|
||||
if (options.dataDecoratorFunc) {
|
||||
data = options.dataDecoratorFunc(
|
||||
req.params.id,
|
||||
'data',
|
||||
|
@ -246,79 +260,20 @@ export const serve_data = {
|
|||
if (fetchTile == null) return res.status(204).send();
|
||||
|
||||
let data = fetchTile.data;
|
||||
const image = new Image();
|
||||
await new Promise(async (resolve, reject) => {
|
||||
image.onload = async () => {
|
||||
const canvas = createCanvas(TILE_SIZE, TILE_SIZE);
|
||||
const context = canvas.getContext('2d');
|
||||
context.drawImage(image, 0, 0);
|
||||
const long = bbox[0];
|
||||
const lat = bbox[1];
|
||||
|
||||
// calculate pixel coordinate of tile,
|
||||
// see https://developers.google.com/maps/documentation/javascript/examples/map-coordinates
|
||||
let siny = Math.sin((lat * Math.PI) / 180);
|
||||
// Truncating to 0.9999 effectively limits latitude to 89.189. This is
|
||||
// about a third of a tile past the edge of the world tile.
|
||||
siny = Math.min(Math.max(siny, -0.9999), 0.9999);
|
||||
const xWorld = TILE_SIZE * (0.5 + long / 360);
|
||||
const yWorld =
|
||||
TILE_SIZE *
|
||||
(0.5 - Math.log((1 + siny) / (1 - siny)) / (4 * Math.PI));
|
||||
|
||||
const scale = 1 << zoom;
|
||||
|
||||
const xTile = Math.floor((xWorld * scale) / TILE_SIZE);
|
||||
const yTile = Math.floor((yWorld * scale) / TILE_SIZE);
|
||||
|
||||
const xPixel = Math.floor(xWorld * scale) - xTile * TILE_SIZE;
|
||||
const yPixel = Math.floor(yWorld * scale) - yTile * TILE_SIZE;
|
||||
if (
|
||||
xPixel < 0 ||
|
||||
yPixel < 0 ||
|
||||
xPixel >= TILE_SIZE ||
|
||||
yPixel >= TILE_SIZE
|
||||
) {
|
||||
return reject('Out of bounds Pixel');
|
||||
}
|
||||
const imgdata = context.getImageData(xPixel, yPixel, 1, 1);
|
||||
const red = imgdata.data[0];
|
||||
const green = imgdata.data[1];
|
||||
const blue = imgdata.data[2];
|
||||
let elevation;
|
||||
if (encoding === 'mapbox') {
|
||||
elevation = -10000 + (red * 256 * 256 + green * 256 + blue) * 0.1;
|
||||
} else if (encoding === 'terrarium') {
|
||||
elevation = red * 256 + green + blue / 256 - 32768;
|
||||
} else {
|
||||
elevation = 'invalid encoding';
|
||||
}
|
||||
resolve(
|
||||
res.status(200).send({
|
||||
var param = {
|
||||
long: bbox[0].toFixed(7),
|
||||
lat: bbox[1].toFixed(7),
|
||||
encoding,
|
||||
format,
|
||||
tile_size: TILE_SIZE,
|
||||
z: zoom,
|
||||
x: xy[0],
|
||||
y: xy[1],
|
||||
red,
|
||||
green,
|
||||
blue,
|
||||
latitude: lat,
|
||||
longitude: long,
|
||||
elevation,
|
||||
}),
|
||||
);
|
||||
};
|
||||
image.onerror = (err) => reject(err);
|
||||
if (format === 'webp') {
|
||||
try {
|
||||
const img = await sharp(data).toFormat('png').toBuffer();
|
||||
image.src = img;
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
} else {
|
||||
image.src = data;
|
||||
}
|
||||
});
|
||||
|
||||
res
|
||||
.status(200)
|
||||
.send(await serve_rendered.getTerrainElevation(data, param));
|
||||
} catch (err) {
|
||||
return res
|
||||
.status(500)
|
||||
|
|
|
@ -6,4 +6,9 @@ export const serve_rendered = {
|
|||
init: (options, repo, programOpts) => {},
|
||||
add: (options, repo, params, id, programOpts, dataResolver) => {},
|
||||
remove: (repo, id) => {},
|
||||
clear: (repo) => {},
|
||||
getTerrainElevation: (data, param) => {
|
||||
param['elevation'] = 'not supported in light';
|
||||
return param;
|
||||
},
|
||||
};
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
// This happens on ARM:
|
||||
// > terminate called after throwing an instance of 'std::runtime_error'
|
||||
// > what(): Cannot read GLX extensions.
|
||||
import 'canvas';
|
||||
import { Image, createCanvas } from 'canvas';
|
||||
import '@maplibre/maplibre-gl-native';
|
||||
//
|
||||
// SECTION END
|
||||
|
@ -15,7 +15,6 @@ import '@maplibre/maplibre-gl-native';
|
|||
import advancedPool from 'advanced-pool';
|
||||
import path from 'path';
|
||||
import url from 'url';
|
||||
import util from 'util';
|
||||
import sharp from 'sharp';
|
||||
import clone from 'clone';
|
||||
import Color from 'color';
|
||||
|
@ -1028,10 +1027,19 @@ export const serve_rendered = {
|
|||
* @param {object} params Parameters object.
|
||||
* @param {string} id ID of the item.
|
||||
* @param {object} programOpts - An object containing the program options
|
||||
* @param {object} style pre-fetched/read StyleJSON object.
|
||||
* @param {Function} dataResolver Function to resolve data.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
add: async function (options, repo, params, id, programOpts, dataResolver) {
|
||||
add: async function (
|
||||
options,
|
||||
repo,
|
||||
params,
|
||||
id,
|
||||
programOpts,
|
||||
style,
|
||||
dataResolver,
|
||||
) {
|
||||
const map = {
|
||||
renderers: [],
|
||||
renderersStatic: [],
|
||||
|
@ -1041,7 +1049,7 @@ export const serve_rendered = {
|
|||
|
||||
const { publicUrl, verbose } = programOpts;
|
||||
|
||||
let styleJSON;
|
||||
const styleJSON = clone(style);
|
||||
/**
|
||||
* Creates a pool of renderers.
|
||||
* @param {number} ratio Pixel ratio
|
||||
|
@ -1230,12 +1238,6 @@ export const serve_rendered = {
|
|||
|
||||
const styleFile = params.style;
|
||||
const styleJSONPath = path.resolve(options.paths.styles, styleFile);
|
||||
try {
|
||||
styleJSON = JSON.parse(await fsp.readFile(styleJSONPath));
|
||||
} catch (e) {
|
||||
console.log('Error parsing style file');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (styleJSON.sprite) {
|
||||
if (!Array.isArray(styleJSON.sprite)) {
|
||||
|
@ -1458,4 +1460,94 @@ export const serve_rendered = {
|
|||
}
|
||||
delete repo[id];
|
||||
},
|
||||
/**
|
||||
* Removes all items from the repository.
|
||||
* @param {object} repo Repository object.
|
||||
* @returns {void}
|
||||
*/
|
||||
clear: function (repo) {
|
||||
Object.keys(repo).forEach((id) => {
|
||||
const item = repo[id];
|
||||
if (item) {
|
||||
item.map.renderers.forEach((pool) => {
|
||||
pool.close();
|
||||
});
|
||||
item.map.renderersStatic.forEach((pool) => {
|
||||
pool.close();
|
||||
});
|
||||
}
|
||||
delete repo[id];
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Get the elevation of terrain tile data by rendering it to a canvas image
|
||||
* @param {object} data The background color (or empty string for transparent).
|
||||
* @param {object} param Required parameters (coordinates e.g.)
|
||||
* @returns {object}
|
||||
*/
|
||||
getTerrainElevation: async function (data, param) {
|
||||
return await new Promise(async (resolve, reject) => {
|
||||
const image = new Image();
|
||||
image.onload = async () => {
|
||||
const canvas = createCanvas(param['tile_size'], param['tile_size']);
|
||||
const context = canvas.getContext('2d');
|
||||
context.drawImage(image, 0, 0);
|
||||
|
||||
// calculate pixel coordinate of tile,
|
||||
// see https://developers.google.com/maps/documentation/javascript/examples/map-coordinates
|
||||
let siny = Math.sin((param['lat'] * Math.PI) / 180);
|
||||
// Truncating to 0.9999 effectively limits latitude to 89.189. This is
|
||||
// about a third of a tile past the edge of the world tile.
|
||||
siny = Math.min(Math.max(siny, -0.9999), 0.9999);
|
||||
const xWorld = param['tile_size'] * (0.5 + param['long'] / 360);
|
||||
const yWorld =
|
||||
param['tile_size'] *
|
||||
(0.5 - Math.log((1 + siny) / (1 - siny)) / (4 * Math.PI));
|
||||
|
||||
const scale = 1 << param['z'];
|
||||
|
||||
const xTile = Math.floor((xWorld * scale) / param['tile_size']);
|
||||
const yTile = Math.floor((yWorld * scale) / param['tile_size']);
|
||||
|
||||
const xPixel = Math.floor(xWorld * scale) - xTile * param['tile_size'];
|
||||
const yPixel = Math.floor(yWorld * scale) - yTile * param['tile_size'];
|
||||
if (
|
||||
xPixel < 0 ||
|
||||
yPixel < 0 ||
|
||||
xPixel >= param['tile_size'] ||
|
||||
yPixel >= param['tile_size']
|
||||
) {
|
||||
return reject('Out of bounds Pixel');
|
||||
}
|
||||
const imgdata = context.getImageData(xPixel, yPixel, 1, 1);
|
||||
const red = imgdata.data[0];
|
||||
const green = imgdata.data[1];
|
||||
const blue = imgdata.data[2];
|
||||
let elevation;
|
||||
if (param['encoding'] === 'mapbox') {
|
||||
elevation = -10000 + (red * 256 * 256 + green * 256 + blue) * 0.1;
|
||||
} else if (param['encoding'] === 'terrarium') {
|
||||
elevation = red * 256 + green + blue / 256 - 32768;
|
||||
} else {
|
||||
elevation = 'invalid encoding';
|
||||
}
|
||||
param['elevation'] = elevation;
|
||||
param['red'] = red;
|
||||
param['green'] = green;
|
||||
param['blue'] = blue;
|
||||
resolve(param);
|
||||
};
|
||||
image.onerror = (err) => reject(err);
|
||||
if (param['format'] === 'webp') {
|
||||
try {
|
||||
const img = await sharp(data).toFormat('png').toBuffer();
|
||||
image.src = img;
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
} else {
|
||||
image.src = data;
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
|
|
@ -196,9 +196,10 @@ export const serve_style = {
|
|||
* @param {object} params Parameters object containing style path
|
||||
* @param {string} id ID of the style.
|
||||
* @param {object} programOpts - An object containing the program options
|
||||
* @param {object} style pre-fetched/read StyleJSON object.
|
||||
* @param {Function} reportTiles Function for reporting tile sources.
|
||||
* @param {Function} reportFont Function for reporting font usage
|
||||
* @returns {boolean} true if add is succesful
|
||||
* @returns {boolean} true if add is successful
|
||||
*/
|
||||
add: function (
|
||||
options,
|
||||
|
@ -206,21 +207,14 @@ export const serve_style = {
|
|||
params,
|
||||
id,
|
||||
programOpts,
|
||||
style,
|
||||
reportTiles,
|
||||
reportFont,
|
||||
) {
|
||||
const { publicUrl } = programOpts;
|
||||
const styleFile = path.resolve(options.paths.styles, params.style);
|
||||
const styleJSON = clone(style);
|
||||
|
||||
let styleFileData;
|
||||
try {
|
||||
styleFileData = fs.readFileSync(styleFile); // TODO: could be made async if this function was
|
||||
} catch (e) {
|
||||
console.log(`Error reading style file "${params.style}"`);
|
||||
return false;
|
||||
}
|
||||
|
||||
const styleJSON = JSON.parse(styleFileData);
|
||||
const validationErrors = validateStyleMin(styleJSON);
|
||||
if (validationErrors.length > 0) {
|
||||
console.log(`The file "${params.style}" is not a valid style file:`);
|
||||
|
|
|
@ -24,13 +24,12 @@ import {
|
|||
} from './utils.js';
|
||||
|
||||
import { fileURLToPath } from 'url';
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const packageJson = JSON.parse(
|
||||
fs.readFileSync(__dirname + '/../package.json', 'utf8'),
|
||||
);
|
||||
|
||||
const isLight = packageJson.name.slice(-6) === '-light';
|
||||
|
||||
const serve_rendered = (
|
||||
await import(`${!isLight ? `./serve_rendered.js` : `./serve_light.js`}`)
|
||||
).serve_rendered;
|
||||
|
@ -179,10 +178,29 @@ async function start(opts) {
|
|||
* @param {object} item - The style configuration object.
|
||||
* @param {boolean} allowMoreData - Whether to allow adding more data sources.
|
||||
* @param {boolean} reportFonts - Whether to report fonts.
|
||||
* @returns {void}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
function addStyle(id, item, allowMoreData, reportFonts) {
|
||||
async function addStyle(id, item, allowMoreData, reportFonts) {
|
||||
let success = true;
|
||||
|
||||
let styleJSON;
|
||||
try {
|
||||
if (isValidHttpUrl(item.style)) {
|
||||
const res = await fetch(item.style);
|
||||
if (!res.ok) {
|
||||
throw new Error(`fetch error ${res.status}`);
|
||||
}
|
||||
styleJSON = await res.json();
|
||||
} else {
|
||||
const styleFile = path.resolve(options.paths.styles, item.style);
|
||||
const styleFileData = await fs.promises.readFile(styleFile);
|
||||
styleJSON = JSON.parse(styleFileData);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(`Error getting style file "${item.style}"`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (item.serve_data !== false) {
|
||||
success = serve_style.add(
|
||||
options,
|
||||
|
@ -190,6 +208,7 @@ async function start(opts) {
|
|||
item,
|
||||
id,
|
||||
opts,
|
||||
styleJSON,
|
||||
(styleSourceId, protocol) => {
|
||||
let dataItemId;
|
||||
for (const id of Object.keys(data)) {
|
||||
|
@ -247,6 +266,7 @@ async function start(opts) {
|
|||
item,
|
||||
id,
|
||||
opts,
|
||||
styleJSON,
|
||||
function dataResolver(styleSourceId) {
|
||||
let fileType;
|
||||
let inputFile;
|
||||
|
@ -272,6 +292,7 @@ async function start(opts) {
|
|||
item.serve_rendered = false;
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
for (const id of Object.keys(config.styles || {})) {
|
||||
|
@ -280,8 +301,7 @@ async function start(opts) {
|
|||
console.log(`Missing "style" property for ${id}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
addStyle(id, item, true, true);
|
||||
startupPromises.push(addStyle(id, item, true, true));
|
||||
}
|
||||
startupPromises.push(
|
||||
serve_font(options, serving.fonts, opts).then((sub) => {
|
||||
|
@ -575,12 +595,15 @@ async function start(opts) {
|
|||
tileJSON.encoding === 'terrarium' ||
|
||||
tileJSON.encoding === 'mapbox'
|
||||
) {
|
||||
if (!isLight) {
|
||||
data.elevation_link = getTileUrls(
|
||||
req,
|
||||
tileJSON.tiles,
|
||||
`data/${id}/elevation`,
|
||||
)[0];
|
||||
}
|
||||
data.is_terrain = true;
|
||||
}
|
||||
if (center) {
|
||||
const centerPx = mercator.px([center[0], center[1]], center[2]);
|
||||
data.thumbnail = `${Math.floor(center[2])}/${Math.floor(
|
||||
|
@ -698,6 +721,7 @@ async function start(opts) {
|
|||
is_terrain: is_terrain,
|
||||
is_terrainrgb: data.tileJSON.encoding === 'mapbox',
|
||||
terrain_encoding: data.tileJSON.encoding,
|
||||
is_light: isLight,
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -740,6 +764,7 @@ async function start(opts) {
|
|||
app,
|
||||
server,
|
||||
startupPromise,
|
||||
serving,
|
||||
};
|
||||
}
|
||||
/**
|
||||
|
@ -772,10 +797,15 @@ export async function server(opts) {
|
|||
console.log(`Caught signal ${signal}, refreshing`);
|
||||
console.log('Stopping server and reloading config');
|
||||
|
||||
running.server.shutdown(() => {
|
||||
const restarted = start(opts);
|
||||
running.server.shutdown(async () => {
|
||||
const restarted = await start(opts);
|
||||
if (!isLight) {
|
||||
serve_rendered.clear(running.serving.rendered);
|
||||
}
|
||||
running.server = restarted.server;
|
||||
running.app = restarted.app;
|
||||
running.startupPromise = restarted.startupPromise;
|
||||
running.serving = restarted.serving;
|
||||
});
|
||||
});
|
||||
return running;
|
||||
|
|
Loading…
Reference in a new issue