Compare commits
No commits in common. "master" and "v5.1.0" have entirely different histories.
37 changed files with 5411 additions and 4111 deletions
1
.github/workflows/automerger.yml
vendored
1
.github/workflows/automerger.yml
vendored
|
@ -14,4 +14,3 @@ jobs:
|
||||||
- uses: fastify/github-action-merge-dependabot@v3
|
- uses: fastify/github-action-merge-dependabot@v3
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
target: minor
|
|
||||||
|
|
5
.github/workflows/ci.yml
vendored
5
.github/workflows/ci.yml
vendored
|
@ -21,11 +21,6 @@ jobs:
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
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 📦
|
- name: Setup node env 📦
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
|
|
18
.github/workflows/ct.yml
vendored
18
.github/workflows/ct.yml
vendored
|
@ -65,22 +65,6 @@ jobs:
|
||||||
context: .
|
context: .
|
||||||
push: false
|
push: false
|
||||||
platforms: linux/arm64,linux/amd64
|
platforms: linux/arm64,linux/amd64
|
||||||
cache-from: type=gha
|
# experimental: https://github.com/docker/build-push-action/blob/master/docs/advanced/cache.md#cache-backend-api
|
||||||
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-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
|
|
11
.gitignore
vendored
11
.gitignore
vendored
|
@ -1,15 +1,4 @@
|
||||||
docs/_build
|
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
|
node_modules
|
||||||
test_data
|
test_data
|
||||||
test_data.zip
|
test_data.zip
|
||||||
|
|
19
CHANGELOG.md
19
CHANGELOG.md
|
@ -1,24 +1,5 @@
|
||||||
# tileserver-gl changelog
|
# 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/1442) by @acalcutt
|
|
||||||
|
|
||||||
## 5.1.0
|
## 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
|
* Update recommended node to v22 + Update docker images to use node 22 (https://github.com/maptiler/tileserver-gl/pull/1438) by @acalcutt
|
||||||
* Upgrade Express to v5 + Canvas to v3 + code cleanup (https://github.com/maptiler/tileserver-gl/pull/1429) by @acalcutt
|
* Upgrade Express to v5 + Canvas to v3 + code cleanup (https://github.com/maptiler/tileserver-gl/pull/1429) by @acalcutt
|
||||||
|
|
90
Dockerfile
90
Dockerfile
|
@ -2,11 +2,10 @@ FROM ubuntu:jammy AS builder
|
||||||
|
|
||||||
ENV NODE_ENV="production"
|
ENV NODE_ENV="production"
|
||||||
|
|
||||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
RUN set -ex; \
|
||||||
|
export DEBIAN_FRONTEND=noninteractive; \
|
||||||
RUN export DEBIAN_FRONTEND=noninteractive && \
|
apt-get -qq update; \
|
||||||
apt-get update && \
|
apt-get -y --no-install-recommends install \
|
||||||
apt-get install -y --no-install-recommends --no-install-suggests \
|
|
||||||
build-essential \
|
build-essential \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
curl \
|
curl \
|
||||||
|
@ -27,17 +26,23 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
|
||||||
librsvg2-common \
|
librsvg2-common \
|
||||||
libcurl4-openssl-dev \
|
libcurl4-openssl-dev \
|
||||||
libpixman-1-dev \
|
libpixman-1-dev \
|
||||||
libpixman-1-0 && \
|
libpixman-1-0; \
|
||||||
mkdir -p /etc/apt/keyrings && \
|
apt-get -y --purge autoremove; \
|
||||||
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \
|
apt-get clean; \
|
||||||
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 && \
|
rm -rf /var/lib/apt/lists/*;
|
||||||
apt-get -qq update && \
|
|
||||||
apt-get install -y --no-install-recommends --no-install-suggests nodejs && \
|
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||||
npm i -g npm@latest && \
|
|
||||||
apt-get -y remove curl gnupg && \
|
RUN mkdir -p /etc/apt/keyrings; \
|
||||||
apt-get -y --purge autoremove && \
|
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg; \
|
||||||
apt-get clean && \
|
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; \
|
||||||
rm -rf /var/lib/apt/lists/*
|
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/*;
|
||||||
|
|
||||||
RUN mkdir -p /usr/src/app
|
RUN mkdir -p /usr/src/app
|
||||||
|
|
||||||
|
@ -46,12 +51,12 @@ WORKDIR /usr/src/app
|
||||||
COPY package.json /usr/src/app
|
COPY package.json /usr/src/app
|
||||||
COPY package-lock.json /usr/src/app
|
COPY package-lock.json /usr/src/app
|
||||||
|
|
||||||
RUN npm config set maxsockets 1 && \
|
RUN npm config set maxsockets 1; \
|
||||||
npm config set fetch-retries 5 && \
|
npm config set fetch-retries 5; \
|
||||||
npm config set fetch-retry-mintimeout 100000 && \
|
npm config set fetch-retry-mintimeout 100000; \
|
||||||
npm config set fetch-retry-maxtimeout 600000 && \
|
npm config set fetch-retry-maxtimeout 600000; \
|
||||||
npm ci --omit=dev && \
|
npm ci --omit=dev; \
|
||||||
chown -R root:root /usr/src/app
|
chown -R root:root /usr/src/app;
|
||||||
|
|
||||||
FROM ubuntu:jammy AS final
|
FROM ubuntu:jammy AS final
|
||||||
|
|
||||||
|
@ -60,13 +65,12 @@ ENV \
|
||||||
CHOKIDAR_USEPOLLING=1 \
|
CHOKIDAR_USEPOLLING=1 \
|
||||||
CHOKIDAR_INTERVAL=500
|
CHOKIDAR_INTERVAL=500
|
||||||
|
|
||||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
RUN set -ex; \
|
||||||
|
export DEBIAN_FRONTEND=noninteractive; \
|
||||||
RUN export DEBIAN_FRONTEND=noninteractive && \
|
groupadd -r node; \
|
||||||
groupadd -r node && \
|
useradd -r -g node node; \
|
||||||
useradd -r -g node node && \
|
apt-get -qq update; \
|
||||||
apt-get -qq update && \
|
apt-get -y --no-install-recommends install \
|
||||||
apt-get install -y --no-install-recommends --no-install-suggests \
|
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
curl \
|
curl \
|
||||||
gnupg \
|
gnupg \
|
||||||
|
@ -81,17 +85,23 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
|
||||||
libpixman-1-0 \
|
libpixman-1-0 \
|
||||||
libcurl4 \
|
libcurl4 \
|
||||||
librsvg2-2 \
|
librsvg2-2 \
|
||||||
libpango-1.0-0 && \
|
libpango-1.0-0; \
|
||||||
mkdir -p /etc/apt/keyrings && \
|
apt-get -y --purge autoremove; \
|
||||||
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \
|
apt-get clean; \
|
||||||
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 && \
|
rm -rf /var/lib/apt/lists/*;
|
||||||
apt-get -qq update && \
|
|
||||||
apt-get install -y --no-install-recommends --no-install-suggests nodejs && \
|
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||||
npm i -g npm@latest && \
|
|
||||||
apt-get -y remove curl gnupg && \
|
RUN mkdir -p /etc/apt/keyrings; \
|
||||||
apt-get -y --purge autoremove && \
|
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg; \
|
||||||
apt-get clean && \
|
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list; \
|
||||||
rm -rf /var/lib/apt/lists/*
|
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/*;
|
||||||
|
|
||||||
COPY --from=builder /usr/src/app /usr/src/app
|
COPY --from=builder /usr/src/app /usr/src/app
|
||||||
|
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
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/*
|
|
105
Dockerfile_light
105
Dockerfile_light
|
@ -1,82 +1,51 @@
|
||||||
FROM ubuntu:jammy AS builder
|
FROM ubuntu:jammy
|
||||||
|
|
||||||
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 \
|
ENV \
|
||||||
NODE_ENV="production" \
|
NODE_ENV="production" \
|
||||||
CHOKIDAR_USEPOLLING=1 \
|
CHOKIDAR_USEPOLLING=1 \
|
||||||
CHOKIDAR_INTERVAL=500
|
CHOKIDAR_INTERVAL=500
|
||||||
|
|
||||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
RUN set -ex; \
|
||||||
|
export DEBIAN_FRONTEND=noninteractive; \
|
||||||
RUN export DEBIAN_FRONTEND=noninteractive && \
|
groupadd -r node; \
|
||||||
groupadd -r node && \
|
useradd -r -g node node; \
|
||||||
useradd -r -g node node && \
|
apt-get -qq update; \
|
||||||
apt-get -qq update && \
|
apt-get -y --no-install-recommends install \
|
||||||
apt-get install -y --no-install-recommends --no-install-suggests \
|
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
curl \
|
curl \
|
||||||
gnupg && \
|
gnupg; \
|
||||||
mkdir -p /etc/apt/keyrings && \
|
mkdir -p /etc/apt/keyrings; \
|
||||||
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \
|
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 && \
|
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 -qq update; \
|
||||||
apt-get install -y --no-install-recommends --no-install-suggests nodejs && \
|
apt-get install -y nodejs; \
|
||||||
npm i -g npm@latest && \
|
npm i -g npm@latest; \
|
||||||
apt-get -y remove curl gnupg && \
|
apt-get -y remove curl gnupg; \
|
||||||
apt-get -y --purge autoremove && \
|
apt-get -y --purge autoremove; \
|
||||||
apt-get clean && \
|
apt-get clean; \
|
||||||
rm -rf /var/lib/apt/lists/*
|
rm -rf /var/lib/apt/lists/*;
|
||||||
|
|
||||||
COPY --from=builder /usr/src/app /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
|
|
||||||
|
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
|
|
||||||
|
RUN mkdir -p /data; \
|
||||||
|
chown node:node /data; \
|
||||||
|
mkdir -p /usr/src/app;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
USER node:node
|
USER node:node
|
||||||
|
|
||||||
ENTRYPOINT ["/usr/src/app/docker-entrypoint.sh"]
|
ENTRYPOINT ["/usr/src/app/docker-entrypoint.sh"]
|
||||||
|
|
|
@ -13,8 +13,7 @@ RUN set -ex; \
|
||||||
unzip \
|
unzip \
|
||||||
build-essential \
|
build-essential \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
curl \
|
wget \
|
||||||
gnupg \
|
|
||||||
pkg-config \
|
pkg-config \
|
||||||
xvfb \
|
xvfb \
|
||||||
libglfw3-dev \
|
libglfw3-dev \
|
||||||
|
@ -26,33 +25,16 @@ RUN set -ex; \
|
||||||
libjpeg-dev \
|
libjpeg-dev \
|
||||||
libgif-dev \
|
libgif-dev \
|
||||||
librsvg2-dev \
|
librsvg2-dev \
|
||||||
gir1.2-rsvg-2.0 \
|
|
||||||
librsvg2-2 \
|
|
||||||
librsvg2-common \
|
|
||||||
libcurl4-openssl-dev \
|
libcurl4-openssl-dev \
|
||||||
libpixman-1-dev \
|
libpixman-1-dev; \
|
||||||
libpixman-1-0; \
|
wget -qO- https://deb.nodesource.com/setup_18.x | bash; \
|
||||||
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; \
|
apt-get install -y nodejs; \
|
||||||
npm i -g npm@latest; \
|
apt-get clean;
|
||||||
apt-get -y remove gnupg; \
|
|
||||||
apt-get -y --purge autoremove; \
|
|
||||||
apt-get clean; \
|
|
||||||
rm -rf /var/lib/apt/lists/*;
|
|
||||||
|
|
||||||
RUN mkdir -p /usr/src/app
|
RUN mkdir -p /usr/src/app
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
RUN curl -L -o test_data.zip https://github.com/maptiler/tileserver-gl/releases/download/v1.3.0/test_data.zip; \
|
RUN wget -O test_data.zip https://github.com/maptiler/tileserver-gl/releases/download/v1.3.0/test_data.zip; \
|
||||||
unzip -q test_data.zip -d test_data
|
unzip -q test_data.zip -d test_data
|
||||||
|
|
||||||
COPY package.json .
|
COPY package.json .
|
||||||
|
|
115
PUBLISHING.md
115
PUBLISHING.md
|
@ -1,112 +1,19 @@
|
||||||
# Publishing a New Version
|
# Publishing new 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.
|
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
|
||||||
|
|
||||||
## Automated Publishing via GitHub Workflow (Recommended)
|
--preid specifies which suffix to use in the release such as pre, next, beta, rc, etc.
|
||||||
|
|
||||||
This is the preferred method for publishing new versions. It automates the entire process, including version bumping, tagging, building, and publishing.
|
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)
|
||||||
|
|
||||||
1. **Prepare the Release:**
|
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.
|
||||||
|
|
||||||
* **Choose the Version Increment:** Determine the appropriate semantic versioning increment (prerelease, prepatch, preminor, premajor, patch, minor, major).
|
For regular versions, you can use patch, minor, or major. E.g. npm version major --no-git-tag-version.
|
||||||
|
|
||||||
* **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.
|
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```
|
||||||
|
|
||||||
```bash
|
3.) Commit and push the changes.
|
||||||
# Example: Increment to a new patch version
|
|
||||||
npm version patch --no-git-tag-version
|
|
||||||
|
|
||||||
# Example: Increment to a new minor version
|
4.) Run the 'Build, Test, Release' github workflow. The workflow will create a NPM, Docker, and Github release and Tag.
|
||||||
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,32 +1,5 @@
|
||||||

|

|
||||||
|
|
||||||
# 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
|
# TileServer GL
|
||||||
[](https://github.com/maptiler/tileserver-gl/actions/workflows/pipeline.yml)
|
[](https://github.com/maptiler/tileserver-gl/actions/workflows/pipeline.yml)
|
||||||
[](https://hub.docker.com/r/maptiler/tileserver-gl/)
|
[](https://hub.docker.com/r/maptiler/tileserver-gl/)
|
||||||
|
|
|
@ -57,9 +57,6 @@ Example:
|
||||||
"tilejson": {
|
"tilejson": {
|
||||||
"format": "webp"
|
"format": "webp"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"remote": {
|
|
||||||
"style": "https://demotiles.maplibre.org/style.json"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"data": {
|
"data": {
|
||||||
|
@ -212,7 +209,7 @@ Not used by default.
|
||||||
|
|
||||||
Each item in this object defines one style (map). It can have the following options:
|
Each item in this object defines one style (map). It can have the following options:
|
||||||
|
|
||||||
* ``style`` -- name of the style json file or url of a remote hosted style [required]
|
* ``style`` -- name of the style json file [required]
|
||||||
* ``serve_rendered`` -- whether to render the raster tiles for this style or not
|
* ``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
|
* ``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
|
* ``tilejson`` -- properties to add to the TileJSON created for the raster data
|
||||||
|
|
|
@ -108,8 +108,6 @@ 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 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
|
||||||
===========
|
===========
|
||||||
* Static files are served at ``/files/{filename}``
|
* Static files are served at ``/files/{filename}``
|
||||||
|
|
4930
package-lock.json
generated
4930
package-lock.json
generated
File diff suppressed because it is too large
Load diff
16
package.json
16
package.json
|
@ -1,17 +1,11 @@
|
||||||
{
|
{
|
||||||
"name": "tileserver-gl",
|
"name": "tileserver-gl",
|
||||||
"version": "5.2.0",
|
"version": "5.1.0",
|
||||||
"description": "Map tile server for JSON GL styles - vector and server side generated raster tiles",
|
"description": "Map tile server for JSON GL styles - vector and server side generated raster tiles",
|
||||||
"main": "src/main.js",
|
"main": "src/main.js",
|
||||||
"bin": "src/main.js",
|
"bin": "src/main.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"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": "mocha test/**.js --timeout 10000 --exit",
|
||||||
"test-docker": "xvfb-run npm test",
|
"test-docker": "xvfb-run npm test",
|
||||||
"lint:yml": "yamllint --schema=CORE_SCHEMA *.{yml,yaml}",
|
"lint:yml": "yamllint --schema=CORE_SCHEMA *.{yml,yaml}",
|
||||||
|
@ -25,30 +19,24 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jsse/pbfont": "^0.2.2",
|
"@jsse/pbfont": "^0.2.2",
|
||||||
"@mapbox/mapbox-gl-rtl-text": "0.3.0",
|
|
||||||
"@mapbox/mbtiles": "0.12.1",
|
"@mapbox/mbtiles": "0.12.1",
|
||||||
"@mapbox/polyline": "^1.2.1",
|
"@mapbox/polyline": "^1.2.1",
|
||||||
"@mapbox/sphericalmercator": "1.2.0",
|
"@mapbox/sphericalmercator": "1.2.0",
|
||||||
"@mapbox/vector-tile": "2.0.3",
|
"@mapbox/vector-tile": "2.0.3",
|
||||||
"@maplibre/maplibre-gl-inspect": "1.7.0",
|
|
||||||
"@maplibre/maplibre-gl-native": "6.0.0",
|
"@maplibre/maplibre-gl-native": "6.0.0",
|
||||||
"@maplibre/maplibre-gl-style-spec": "20.3.1",
|
"@maplibre/maplibre-gl-style-spec": "20.3.1",
|
||||||
"@sindresorhus/fnv1a": "3.1.0",
|
"@sindresorhus/fnv1a": "3.1.0",
|
||||||
"advanced-pool": "0.3.3",
|
"advanced-pool": "0.3.3",
|
||||||
"axios": "^1.8.2",
|
"axios": "^1.7.7",
|
||||||
"canvas": "3.0.1",
|
"canvas": "3.0.1",
|
||||||
"chokidar": "3.6.0",
|
"chokidar": "3.6.0",
|
||||||
"clone": "2.1.2",
|
"clone": "2.1.2",
|
||||||
"color": "4.2.3",
|
"color": "4.2.3",
|
||||||
"commander": "12.1.0",
|
"commander": "12.1.0",
|
||||||
"copyfiles": "2.4.1",
|
|
||||||
"cors": "2.8.5",
|
"cors": "2.8.5",
|
||||||
"express": "5.0.1",
|
"express": "5.0.1",
|
||||||
"handlebars": "4.7.8",
|
"handlebars": "4.7.8",
|
||||||
"http-shutdown": "1.2.2",
|
"http-shutdown": "1.2.2",
|
||||||
"leaflet": "1.9.4",
|
|
||||||
"leaflet-hash": "0.2.1",
|
|
||||||
"maplibre-gl": "4.7.1",
|
|
||||||
"morgan": "1.10.0",
|
"morgan": "1.10.0",
|
||||||
"pbf": "4.0.1",
|
"pbf": "4.0.1",
|
||||||
"pmtiles": "3.0.7",
|
"pmtiles": "3.0.7",
|
||||||
|
|
|
@ -18,7 +18,7 @@ class ElevationInfoControl {
|
||||||
|
|
||||||
map.on('click', (e) => {
|
map.on('click', (e) => {
|
||||||
var url = this.url;
|
var url = this.url;
|
||||||
var coord = {"z": Math.floor(map.getZoom()), "x": e.lngLat["lng"].toFixed(7), "y": e.lngLat["lat"].toFixed(7)};
|
var coord = {"z": Math.floor(map.getZoom()), "x": e.lngLat["lng"], "y": e.lngLat["lat"]};
|
||||||
for(var key in coord) {
|
for(var key in coord) {
|
||||||
url = url.replace(new RegExp('{'+ key +'}','g'), coord[key]);
|
url = url.replace(new RegExp('{'+ key +'}','g'), coord[key]);
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
162
public/resources/leaflet-hash.js
Normal file
162
public/resources/leaflet-hash.js
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
(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);
|
661
public/resources/leaflet.css
Normal file
661
public/resources/leaflet.css
Normal file
|
@ -0,0 +1,661 @@
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
}
|
6
public/resources/leaflet.js
Normal file
6
public/resources/leaflet.js
Normal file
File diff suppressed because one or more lines are too long
1
public/resources/leaflet.js.map
Normal file
1
public/resources/leaflet.js.map
Normal file
File diff suppressed because one or more lines are too long
6
public/resources/mapbox-gl-rtl-text.js
Normal file
6
public/resources/mapbox-gl-rtl-text.js
Normal file
File diff suppressed because one or more lines are too long
40
public/resources/maplibre-gl-inspect.css
Normal file
40
public/resources/maplibre-gl-inspect.css
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
.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");
|
||||||
|
}
|
2858
public/resources/maplibre-gl-inspect.js
Normal file
2858
public/resources/maplibre-gl-inspect.js
Normal file
File diff suppressed because it is too large
Load diff
1
public/resources/maplibre-gl-inspect.js.map
Normal file
1
public/resources/maplibre-gl-inspect.js.map
Normal file
File diff suppressed because one or more lines are too long
1
public/resources/maplibre-gl.css
Normal file
1
public/resources/maplibre-gl.css
Normal file
File diff suppressed because one or more lines are too long
59
public/resources/maplibre-gl.js
Normal file
59
public/resources/maplibre-gl.js
Normal file
File diff suppressed because one or more lines are too long
1
public/resources/maplibre-gl.js.map
Normal file
1
public/resources/maplibre-gl.js.map
Normal file
File diff suppressed because one or more lines are too long
|
@ -9,9 +9,7 @@
|
||||||
<link rel="stylesheet" type="text/css" href="{{public_url}}maplibre-gl-inspect.css{{&key_query}}" />
|
<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.js{{&key_query}}"></script>
|
||||||
<script src="{{public_url}}maplibre-gl-inspect.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>
|
<script src="{{public_url}}elevation-control.js{{&key_query}}"></script>
|
||||||
{{/is_light}}
|
|
||||||
<style>
|
<style>
|
||||||
body {background:#fff;color:#333;font-family:Arial, sans-serif;}
|
body {background:#fff;color:#333;font-family:Arial, sans-serif;}
|
||||||
{{^is_terrain}}
|
{{^is_terrain}}
|
||||||
|
@ -23,9 +21,7 @@
|
||||||
h1 {position:absolute;top:5px;right:0;width:240px;margin:0;line-height:20px;font-size:20px;}
|
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 {position:absolute;top:35px;right:0;bottom:0;width:240px;overflow:auto;}
|
||||||
#layerList div div {width:15px;height:15px;display:inline-block;}
|
#layerList div div {width:15px;height:15px;display:inline-block;}
|
||||||
{{^is_light}}
|
|
||||||
.maplibre-ctrl-elevation { padding-left: 5px; padding-right: 5px; }
|
.maplibre-ctrl-elevation { padding-left: 5px; padding-right: 5px; }
|
||||||
{{/is_light}}
|
|
||||||
</style>
|
</style>
|
||||||
{{/use_maplibre}}
|
{{/use_maplibre}}
|
||||||
{{^use_maplibre}}
|
{{^use_maplibre}}
|
||||||
|
@ -139,13 +135,11 @@
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
{{^is_light}}
|
|
||||||
map.addControl(
|
map.addControl(
|
||||||
new ElevationInfoControl({
|
new ElevationInfoControl({
|
||||||
url: "{{public_url}}data/{{id}}/elevation/{z}/{x}/{y}"
|
url: "{{public_url}}data/{{id}}/elevation/{z}/{x}/{y}"
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
{{/is_light}}
|
|
||||||
{{/is_terrain}}
|
{{/is_terrain}}
|
||||||
{{^is_terrain}}
|
{{^is_terrain}}
|
||||||
|
|
||||||
|
|
|
@ -124,9 +124,9 @@
|
||||||
{{/is_vector}}
|
{{/is_vector}}
|
||||||
{{^is_vector}}
|
{{^is_vector}}
|
||||||
<a class="btn" href="{{public_url}}data/{{@key}}/{{&../key_query}}{{viewer_hash}}">View</a>
|
<a class="btn" href="{{public_url}}data/{{@key}}/{{&../key_query}}{{viewer_hash}}">View</a>
|
||||||
{{#is_terrain}}
|
{{#elevation_link}}
|
||||||
<a class="btn" href="{{public_url}}data/preview/{{@key}}/{{&../key_query}}{{viewer_hash}}">Preview Terrain</a>
|
<a class="btn" href="{{public_url}}data/preview/{{@key}}/{{&../key_query}}{{viewer_hash}}">Preview Terrain</a>
|
||||||
{{/is_terrain}}
|
{{/elevation_link}}
|
||||||
{{/is_vector}}
|
{{/is_vector}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -8,6 +8,8 @@ import express from 'express';
|
||||||
import Pbf from 'pbf';
|
import Pbf from 'pbf';
|
||||||
import { VectorTile } from '@mapbox/vector-tile';
|
import { VectorTile } from '@mapbox/vector-tile';
|
||||||
import SphericalMercator from '@mapbox/sphericalmercator';
|
import SphericalMercator from '@mapbox/sphericalmercator';
|
||||||
|
import { Image, createCanvas } from 'canvas';
|
||||||
|
import sharp from 'sharp';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
fixTileJSONCenter,
|
fixTileJSONCenter,
|
||||||
|
@ -19,21 +21,6 @@ import { getPMtilesInfo, openPMtiles } from './pmtiles_adapter.js';
|
||||||
import { gunzipP, gzipP } from './promises.js';
|
import { gunzipP, gzipP } from './promises.js';
|
||||||
import { openMbTilesWrapper } from './mbtiles_wrapper.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 = {
|
export const serve_data = {
|
||||||
/**
|
/**
|
||||||
* Initializes the serve_data module.
|
* Initializes the serve_data module.
|
||||||
|
@ -114,13 +101,12 @@ export const serve_data = {
|
||||||
let headers = fetchTile.headers;
|
let headers = fetchTile.headers;
|
||||||
let isGzipped = data.slice(0, 2).indexOf(Buffer.from([0x1f, 0x8b])) === 0;
|
let isGzipped = data.slice(0, 2).indexOf(Buffer.from([0x1f, 0x8b])) === 0;
|
||||||
|
|
||||||
if (isGzipped) {
|
|
||||||
data = await gunzipP(data);
|
|
||||||
isGzipped = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tileJSONFormat === 'pbf') {
|
if (tileJSONFormat === 'pbf') {
|
||||||
if (options.dataDecoratorFunc) {
|
if (options.dataDecoratorFunc) {
|
||||||
|
if (isGzipped) {
|
||||||
|
data = await gunzipP(data);
|
||||||
|
isGzipped = false;
|
||||||
|
}
|
||||||
data = options.dataDecoratorFunc(
|
data = options.dataDecoratorFunc(
|
||||||
req.params.id,
|
req.params.id,
|
||||||
'data',
|
'data',
|
||||||
|
@ -260,20 +246,79 @@ export const serve_data = {
|
||||||
if (fetchTile == null) return res.status(204).send();
|
if (fetchTile == null) return res.status(204).send();
|
||||||
|
|
||||||
let data = fetchTile.data;
|
let data = fetchTile.data;
|
||||||
var param = {
|
const image = new Image();
|
||||||
long: bbox[0].toFixed(7),
|
await new Promise(async (resolve, reject) => {
|
||||||
lat: bbox[1].toFixed(7),
|
image.onload = async () => {
|
||||||
encoding,
|
const canvas = createCanvas(TILE_SIZE, TILE_SIZE);
|
||||||
format,
|
const context = canvas.getContext('2d');
|
||||||
tile_size: TILE_SIZE,
|
context.drawImage(image, 0, 0);
|
||||||
z: zoom,
|
const long = bbox[0];
|
||||||
x: xy[0],
|
const lat = bbox[1];
|
||||||
y: xy[1],
|
|
||||||
};
|
|
||||||
|
|
||||||
res
|
// calculate pixel coordinate of tile,
|
||||||
.status(200)
|
// see https://developers.google.com/maps/documentation/javascript/examples/map-coordinates
|
||||||
.send(await serve_rendered.getTerrainElevation(data, param));
|
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({
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return res
|
return res
|
||||||
.status(500)
|
.status(500)
|
||||||
|
|
|
@ -6,9 +6,4 @@ export const serve_rendered = {
|
||||||
init: (options, repo, programOpts) => {},
|
init: (options, repo, programOpts) => {},
|
||||||
add: (options, repo, params, id, programOpts, dataResolver) => {},
|
add: (options, repo, params, id, programOpts, dataResolver) => {},
|
||||||
remove: (repo, id) => {},
|
remove: (repo, id) => {},
|
||||||
clear: (repo) => {},
|
|
||||||
getTerrainElevation: (data, param) => {
|
|
||||||
param['elevation'] = 'not supported in light';
|
|
||||||
return param;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
// This happens on ARM:
|
// This happens on ARM:
|
||||||
// > terminate called after throwing an instance of 'std::runtime_error'
|
// > terminate called after throwing an instance of 'std::runtime_error'
|
||||||
// > what(): Cannot read GLX extensions.
|
// > what(): Cannot read GLX extensions.
|
||||||
import { Image, createCanvas } from 'canvas';
|
import 'canvas';
|
||||||
import '@maplibre/maplibre-gl-native';
|
import '@maplibre/maplibre-gl-native';
|
||||||
//
|
//
|
||||||
// SECTION END
|
// SECTION END
|
||||||
|
@ -15,6 +15,7 @@ import '@maplibre/maplibre-gl-native';
|
||||||
import advancedPool from 'advanced-pool';
|
import advancedPool from 'advanced-pool';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
|
import util from 'util';
|
||||||
import sharp from 'sharp';
|
import sharp from 'sharp';
|
||||||
import clone from 'clone';
|
import clone from 'clone';
|
||||||
import Color from 'color';
|
import Color from 'color';
|
||||||
|
@ -1027,19 +1028,10 @@ export const serve_rendered = {
|
||||||
* @param {object} params Parameters object.
|
* @param {object} params Parameters object.
|
||||||
* @param {string} id ID of the item.
|
* @param {string} id ID of the item.
|
||||||
* @param {object} programOpts - An object containing the program options
|
* @param {object} programOpts - An object containing the program options
|
||||||
* @param {object} style pre-fetched/read StyleJSON object.
|
|
||||||
* @param {Function} dataResolver Function to resolve data.
|
* @param {Function} dataResolver Function to resolve data.
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
add: async function (
|
add: async function (options, repo, params, id, programOpts, dataResolver) {
|
||||||
options,
|
|
||||||
repo,
|
|
||||||
params,
|
|
||||||
id,
|
|
||||||
programOpts,
|
|
||||||
style,
|
|
||||||
dataResolver,
|
|
||||||
) {
|
|
||||||
const map = {
|
const map = {
|
||||||
renderers: [],
|
renderers: [],
|
||||||
renderersStatic: [],
|
renderersStatic: [],
|
||||||
|
@ -1049,7 +1041,7 @@ export const serve_rendered = {
|
||||||
|
|
||||||
const { publicUrl, verbose } = programOpts;
|
const { publicUrl, verbose } = programOpts;
|
||||||
|
|
||||||
const styleJSON = clone(style);
|
let styleJSON;
|
||||||
/**
|
/**
|
||||||
* Creates a pool of renderers.
|
* Creates a pool of renderers.
|
||||||
* @param {number} ratio Pixel ratio
|
* @param {number} ratio Pixel ratio
|
||||||
|
@ -1238,6 +1230,12 @@ export const serve_rendered = {
|
||||||
|
|
||||||
const styleFile = params.style;
|
const styleFile = params.style;
|
||||||
const styleJSONPath = path.resolve(options.paths.styles, styleFile);
|
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 (styleJSON.sprite) {
|
||||||
if (!Array.isArray(styleJSON.sprite)) {
|
if (!Array.isArray(styleJSON.sprite)) {
|
||||||
|
@ -1460,94 +1458,4 @@ export const serve_rendered = {
|
||||||
}
|
}
|
||||||
delete repo[id];
|
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,10 +196,9 @@ export const serve_style = {
|
||||||
* @param {object} params Parameters object containing style path
|
* @param {object} params Parameters object containing style path
|
||||||
* @param {string} id ID of the style.
|
* @param {string} id ID of the style.
|
||||||
* @param {object} programOpts - An object containing the program options
|
* @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} reportTiles Function for reporting tile sources.
|
||||||
* @param {Function} reportFont Function for reporting font usage
|
* @param {Function} reportFont Function for reporting font usage
|
||||||
* @returns {boolean} true if add is successful
|
* @returns {boolean} true if add is succesful
|
||||||
*/
|
*/
|
||||||
add: function (
|
add: function (
|
||||||
options,
|
options,
|
||||||
|
@ -207,14 +206,21 @@ export const serve_style = {
|
||||||
params,
|
params,
|
||||||
id,
|
id,
|
||||||
programOpts,
|
programOpts,
|
||||||
style,
|
|
||||||
reportTiles,
|
reportTiles,
|
||||||
reportFont,
|
reportFont,
|
||||||
) {
|
) {
|
||||||
const { publicUrl } = programOpts;
|
const { publicUrl } = programOpts;
|
||||||
const styleFile = path.resolve(options.paths.styles, params.style);
|
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);
|
const validationErrors = validateStyleMin(styleJSON);
|
||||||
if (validationErrors.length > 0) {
|
if (validationErrors.length > 0) {
|
||||||
console.log(`The file "${params.style}" is not a valid style file:`);
|
console.log(`The file "${params.style}" is not a valid style file:`);
|
||||||
|
|
|
@ -24,12 +24,13 @@ import {
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
|
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = path.dirname(__filename);
|
||||||
const packageJson = JSON.parse(
|
const packageJson = JSON.parse(
|
||||||
fs.readFileSync(__dirname + '/../package.json', 'utf8'),
|
fs.readFileSync(__dirname + '/../package.json', 'utf8'),
|
||||||
);
|
);
|
||||||
const isLight = packageJson.name.slice(-6) === '-light';
|
|
||||||
|
|
||||||
|
const isLight = packageJson.name.slice(-6) === '-light';
|
||||||
const serve_rendered = (
|
const serve_rendered = (
|
||||||
await import(`${!isLight ? `./serve_rendered.js` : `./serve_light.js`}`)
|
await import(`${!isLight ? `./serve_rendered.js` : `./serve_light.js`}`)
|
||||||
).serve_rendered;
|
).serve_rendered;
|
||||||
|
@ -178,29 +179,10 @@ async function start(opts) {
|
||||||
* @param {object} item - The style configuration object.
|
* @param {object} item - The style configuration object.
|
||||||
* @param {boolean} allowMoreData - Whether to allow adding more data sources.
|
* @param {boolean} allowMoreData - Whether to allow adding more data sources.
|
||||||
* @param {boolean} reportFonts - Whether to report fonts.
|
* @param {boolean} reportFonts - Whether to report fonts.
|
||||||
* @returns {Promise<void>}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
async function addStyle(id, item, allowMoreData, reportFonts) {
|
function addStyle(id, item, allowMoreData, reportFonts) {
|
||||||
let success = true;
|
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) {
|
if (item.serve_data !== false) {
|
||||||
success = serve_style.add(
|
success = serve_style.add(
|
||||||
options,
|
options,
|
||||||
|
@ -208,7 +190,6 @@ async function start(opts) {
|
||||||
item,
|
item,
|
||||||
id,
|
id,
|
||||||
opts,
|
opts,
|
||||||
styleJSON,
|
|
||||||
(styleSourceId, protocol) => {
|
(styleSourceId, protocol) => {
|
||||||
let dataItemId;
|
let dataItemId;
|
||||||
for (const id of Object.keys(data)) {
|
for (const id of Object.keys(data)) {
|
||||||
|
@ -266,7 +247,6 @@ async function start(opts) {
|
||||||
item,
|
item,
|
||||||
id,
|
id,
|
||||||
opts,
|
opts,
|
||||||
styleJSON,
|
|
||||||
function dataResolver(styleSourceId) {
|
function dataResolver(styleSourceId) {
|
||||||
let fileType;
|
let fileType;
|
||||||
let inputFile;
|
let inputFile;
|
||||||
|
@ -292,7 +272,6 @@ async function start(opts) {
|
||||||
item.serve_rendered = false;
|
item.serve_rendered = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const id of Object.keys(config.styles || {})) {
|
for (const id of Object.keys(config.styles || {})) {
|
||||||
|
@ -301,7 +280,8 @@ async function start(opts) {
|
||||||
console.log(`Missing "style" property for ${id}`);
|
console.log(`Missing "style" property for ${id}`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
startupPromises.push(addStyle(id, item, true, true));
|
|
||||||
|
addStyle(id, item, true, true);
|
||||||
}
|
}
|
||||||
startupPromises.push(
|
startupPromises.push(
|
||||||
serve_font(options, serving.fonts, opts).then((sub) => {
|
serve_font(options, serving.fonts, opts).then((sub) => {
|
||||||
|
@ -595,14 +575,11 @@ async function start(opts) {
|
||||||
tileJSON.encoding === 'terrarium' ||
|
tileJSON.encoding === 'terrarium' ||
|
||||||
tileJSON.encoding === 'mapbox'
|
tileJSON.encoding === 'mapbox'
|
||||||
) {
|
) {
|
||||||
if (!isLight) {
|
data.elevation_link = getTileUrls(
|
||||||
data.elevation_link = getTileUrls(
|
req,
|
||||||
req,
|
tileJSON.tiles,
|
||||||
tileJSON.tiles,
|
`data/${id}/elevation`,
|
||||||
`data/${id}/elevation`,
|
)[0];
|
||||||
)[0];
|
|
||||||
}
|
|
||||||
data.is_terrain = true;
|
|
||||||
}
|
}
|
||||||
if (center) {
|
if (center) {
|
||||||
const centerPx = mercator.px([center[0], center[1]], center[2]);
|
const centerPx = mercator.px([center[0], center[1]], center[2]);
|
||||||
|
@ -721,7 +698,6 @@ async function start(opts) {
|
||||||
is_terrain: is_terrain,
|
is_terrain: is_terrain,
|
||||||
is_terrainrgb: data.tileJSON.encoding === 'mapbox',
|
is_terrainrgb: data.tileJSON.encoding === 'mapbox',
|
||||||
terrain_encoding: data.tileJSON.encoding,
|
terrain_encoding: data.tileJSON.encoding,
|
||||||
is_light: isLight,
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -764,7 +740,6 @@ async function start(opts) {
|
||||||
app,
|
app,
|
||||||
server,
|
server,
|
||||||
startupPromise,
|
startupPromise,
|
||||||
serving,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
@ -797,15 +772,10 @@ export async function server(opts) {
|
||||||
console.log(`Caught signal ${signal}, refreshing`);
|
console.log(`Caught signal ${signal}, refreshing`);
|
||||||
console.log('Stopping server and reloading config');
|
console.log('Stopping server and reloading config');
|
||||||
|
|
||||||
running.server.shutdown(async () => {
|
running.server.shutdown(() => {
|
||||||
const restarted = await start(opts);
|
const restarted = start(opts);
|
||||||
if (!isLight) {
|
|
||||||
serve_rendered.clear(running.serving.rendered);
|
|
||||||
}
|
|
||||||
running.server = restarted.server;
|
running.server = restarted.server;
|
||||||
running.app = restarted.app;
|
running.app = restarted.app;
|
||||||
running.startupPromise = restarted.startupPromise;
|
|
||||||
running.serving = restarted.serving;
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return running;
|
return running;
|
||||||
|
|
Loading…
Reference in a new issue