Arma raster tiles

Servidor estático de tiles raster XYZ para os mapas de Arma 3 fornecidos como KML/KMZ GroundOverlay. Todas as fontes compõem uma camada mundial única: não existe identificador de mapa na URL.

Arquitetura

tile-buildersource/ recursivamente, extrai KMZs somente em diretórios temporários, lê cada GroundOverlay, localiza a imagem local indicada por Icon/href e a reprojeta para Web Mercator (EPSG:3857). Para LatLonBox com rotation, os quatro cantos são girados no sentido anti-horário em torno do centro antes do warp GDAL; o raster final recebe alfa para conservar transparência e recortar as bordas rotacionadas.

Cada overlay gera tiles temporários por gdal2tiles --xyz. O builder os compõe com alfa em uma única pirâmide. Uma área menor tem prioridade e é desenhada por cima; se as áreas forem iguais, vence primeiro o caminho relativo em ordem alfabética e depois o índice do GroundOverlay. A área é calculada a partir do polígono rotacionado em EPSG:3857.

O resultado físico é publicado diretamente na pasta local tiles/:

tiles/{z}/{x}/{y}.png
tiles/metadata.json

O Nginx aceita exclusivamente a rota pública abaixo e faz a tradução interna. Não publique nem use a ordem de armazenamento diretamente.

https://arma_tiles.valmo.dev/tiles/{x}/{y}/{z}
https://arma_tiles.valmo.dev/tiles/18342/12417/15

Mesmo sem .png na URL, a resposta é Content-Type: image/png e inclui Access-Control-Allow-Origin: *, permitindo uso direto pelo MapLibre em outra origem. Sem provider configurado, coordenadas sem cobertura retornam tiles/empty.png; com provider configurado, são encaminhadas ao callback e armazenadas no cache local do Nginx.

Provider de callback e MapLibre

Copie .env.example para .env e, se quiser uma camada base, defina um template HTTPS XYZ:

CALLBACK_PROVIDER=https://tiles.seu-provider.example/{z}/{x}/{y}.png

O template é validado antes de qualquer leitura/processamento de fonte. Ele precisa usar https, não pode ter query, credenciais ou IP privado, e deve conter exatamente um {z}, {x} e {y} no caminho. Deixe a variável vazia ou ausente para usar o empty.png branco.

Para que o provider apareça tanto onde não há mapa Arma quanto nas bordas transparentes de uma tile parcialmente coberta, use a camada base local abaixo da camada Arma:

sources: {
  providerBase: {
    type: "raster",
    tiles: ["http://localhost:9000/base/{x}/{y}/{z}"],
    tileSize: 256,
    scheme: "xyz"
  },
  arma: {
    type: "raster",
    tiles: ["http://localhost:9000/tiles/{x}/{y}/{z}"],
    tileSize: 256,
    scheme: "xyz",
    minzoom: 0,
    maxzoom: 17
  }
},
layers: [
  { id: "provider-base", type: "raster", source: "providerBase" },
  { id: "arma", type: "raster", source: "arma" }
]

/base/{x}/{y}/{z} e o fallback de /tiles/ consultam o callback sob demanda e compartilham um cache local persistente. Exiba a atribuição e cumpra os termos definidos pelo provider escolhido.

Entradas

Coloque .kmz e .kml em source/, inclusive em subpastas. Um KMZ deve conter um KML (o builder prefere doc.kml) e a imagem local referenciada pelo KML. KMLs soltos podem referenciar imagens dentro da mesma árvore de source/.

Os arquivos de entrada nunca são modificados ou removidos. Erros por arquivo são registrados em tiles/metadata.json e não bloqueiam as demais fontes. Links HTTP(S), caminhos absolutos e gx:LatLonQuad não fazem parte desta primeira versão; use LatLonBox e imagens locais.

Zoom e configuração

O máximo automático de cada overlay é calculado a partir da maior resolução reprojetada em metros por pixel. O builder escolhe o maior zoom cuja resolução de tile ainda não é mais detalhada que a fonte, evitando ampliação de pixels.

O mínimo automático é 0: os níveis amplos custam pouco e tornam mapas isolados encontráveis a partir da visão mundial. Cada fonte para no próprio máximo nativo, evitando ampliar pixels no servidor. Para permitir zoom visual adicional, configure o MapLibre com maxZoom alto e mantenha o maxzoom da source no máximo nativo; o MapLibre amplia a última tile disponível. Se nenhuma fonte cobrir a coordenada, o servidor usa o callback configurado ou o empty.png branco.

config/maps.json permite overrides globais e por caminho relativo a source/:

{
  "global": { "minzoom": null, "maxzoom": null },
  "sources": {
    "Altis.kmz": { "minzoom": 8, "maxzoom": 16 },
    "subpasta/exemplo.kml": { "maxzoom": 14 }
  }
}

Use inteiro entre 0 e 22 ou null. A precedência é fonte, global, cálculo automático. Um maxzoom explícito acima do máximo automático é permitido e ficará marcado em metadata.json, pois solicita ampliação conscientemente.

Geração local

Dependências locais: Python 3, Pillow, GDAL com gdal_translate, gdalwarp, gdalinfo e gdal2tiles.py.

./scripts/build-tiles.sh
./scripts/build-tiles.sh --source-filter Altis.kmz

O cache em cache/ usa SHA-256 da fonte. Entradas inalteradas reutilizam os GeoTIFFs reprojetados. Para garantir a composição e a prioridade corretas, a v1 recompõe a pirâmide final inteira em staging a cada build; apenas a reprojeção das fontes inalteradas é reutilizada. Ao fim do build, a pasta local tiles/ é substituída pelo resultado completo, após a geração de metadata.json.

Docker Compose e Coolify

Para desenvolvimento local:

docker compose up --build

O Nginx ficará disponível em http://localhost:${HTTP_PORT:-9000}. O Compose executa primeiro o builder e monta a pasta tiles/ como somente leitura no Nginx.

No Coolify, mantenha as entradas fora do Git e crie uma pasta/volume persistente no host. Defina ARMA_TILES_SOURCE_DIR para esse caminho; ele será montado como /app/source somente leitura. O volume nomeado arma_tiles_data preserva o cache entre deploys; a pasta tiles/ do projeto é o volume de saída publicado tanto pelo builder quanto pelo Nginx. Configure o domínio arma_tiles.valmo.dev e TLS no proxy do Coolify, apontando para a porta 80 do serviço tiles.

Para limitar paralelismo do GDAL, configure GDAL2TILES_PROCESSES (o padrão é 1). As respostas de tile têm cache público de um dia e stale-while-revalidate de sete dias, além de ETag.

Validação

python3 tests/test_build_tiles.py
./scripts/smoke-test.sh

O teste de fumaça processa Altis.kmz, verifica tiles/metadata.json e um PNG, sobe o Nginx e consulta uma tile pela ordem pública {x}/{y}/{z} para confirmar 200, image/png e CORS. O callback é opcional e não é exercitado pelo teste local.

Limitações conhecidas

  • Não há watcher: após adicionar ou alterar uma entrada, execute o builder ou faça um novo deploy.
  • A composição final ainda é uma reconstrução global; a estrutura de cache permite uma futura invalidação apenas dos tiles afetados.
  • Esta versão não baixa imagens externas, não suporta gx:LatLonQuad, não trata overlays que cruzam o antimeridiano e não inclui DEM, hillshade, vetores, MBTiles, frontend, autenticação ou banco de dados.
S
Description
Map tile server for ArmaTAK maps (or any usecase for arma maps)
Readme 90 KiB
Languages
Python 92.3%
Shell 5%
Dockerfile 2.7%