基于geojson-vt和canvas的高性能出图

概述

本文介绍基于geojson-vtcanvas,实现node端高性能出图。

效果

实现

1. canvas绘图

js 复制代码
import { createCanvas } from 'canvas'

const tileSize = 256;
const canvas = createCanvas(tileSize, tileSize)
const ctx = canvas.getContext('2d')

2. 处理geojson

js 复制代码
const geojson = result.rows[0].geojson;
geojson.features = geojson.features || []
const tileIndex = geojsonvt(geojson, {
    maxZoom: 22,
    tolerance: 3, // simplification tolerance (higher means simpler)
    extent: 4096, // tile extent (both width and height)
    buffer: 64,   // tile buffer on each side
    debug: 0,     // logging level (0 to disable, 1 or 2)
    lineMetrics: false, // whether to enable line metrics tracking for LineString/MultiLineString features
    promoteId: null,    // name of a feature property to promote to feature.id. Cannot be used with `generateId`
    generateId: false,  // whether to generate feature ids. Cannot be used with `promoteId`
    indexMaxZoom: 5,       // max zoom in the initial tile index
    indexMaxPoints: 100000 // max number of points per tile in the index
})
const features = tileIndex.getTile(Number(z), Number(x), Number(y))?.features || [];

3. 绘制features

js 复制代码
function drawTile(features, zoom) {
    ctx.clearRect(0, 0, tileSize, tileSize);
    // 绘制边框
    // ctx.strokeStyle = '#fff'
    // ctx.lineWidth = 1
    // ctx.strokeRect(0, 0, tileSize, tileSize)
    for (let i = 0; i < features.length; i++) {
        const feature = features[i];
        const {adcode, name} = feature.tags
        const type = feature.type;
        ctx.beginPath();
        for (let j = 0; j < feature.geometry.length; j++) {
            const geom = feature.geometry[j];
            if (type === 1) { // 1点
                ctx.save()
                ctx.fillStyle = `rgba(${'255,0,0'}, 1)`;
                ctx.strokeStyle = '#fff'
                ctx.lineWidth = 2;
                ctx.textAlign = "center";
                ctx.textBaseline = "middle"
                ctx.font = "bold 16px 宋体";
                // const len = ctx.measureText(name).width / 2;
                // const offset = 5
                // if(x + len > tileSize) x = tileSize - len - offset
                // if(x - len < 0 ) x = len + offset
                if(name && zoom >= 9) {
                    ctx.strokeText(name, geom[0] / 16.0, geom[1] / 16.0)
                    ctx.fillText(name, geom[0] / 16.0, geom[1] / 16.0)
                }
                // ctx.arc(geom[0] / 16.0, geom[1] / 16.0, 3, 0, 2 * Math.PI, false);
                ctx.restore()
                ctx.fill()
                ctx.stroke()
            } else { // 2线 或 3面
                // const color = colorDict[adcode] || '255,0,0'
                const color = '255,0,0'
                ctx.strokeStyle = `rgba(${color}, 1)`;
                ctx.fillStyle = `rgba(${color}, 0.1)`;
                ctx.lineWidth = 1;
                for (let k = 0; k < geom.length; k++) {
                    const p = geom[k];
                    if (k) ctx.lineTo(p[0] / 16.0, p[1] / 16.0);
                    else ctx.moveTo(p[0] / 16.0, p[1] / 16.0);
                }
                // if (type === 3) ctx.fill();
                ctx.stroke();
            }
        }
    }
}

4. 设置缓存并发送到前端

js 复制代码
app.get('/tile/:z/:x/:y', (req, res) => {
    const {z, x, y} = req.params
    try {
        getFeatures(x, y, z).then(image => {
            res.setHeader('Expires', new Date(Date.now() + 30 * 1000).toUTCString())
            res.writeHead(200, {
                "Content-Type": "image/png",
            });
            res.end(image);
        })
    } catch (err) {
        console.error(err);
    }   
})

5. 数据获取

数据是存在PG数据库中,可通过node连接获取,可通过如下语句直接将结果转换为geojson。

sql 复制代码
SELECT json_build_object(
   'type', 'FeatureCollection',
   'features', json_agg(ST_AsGeoJSON(t.*)::json)
) as geojson FROM (
   select adcode, name, geom from base_county where st_intersects(BBox(101, 52, 7), geom)
) as t(adcode, name, geom);

6. 前端调用

js 复制代码
new ol.layer.Tile({
  source: new ol.source.XYZ({
      url: 'http://127.0.0.1:18089/tile/{z}/{x}/{y}'
  })
}),
相关推荐
aiguangyuan5 天前
Node项目中两个常用的环境变量配置工具
node·后端开发
自律的蜗牛8 天前
Systemd(Linux 系统级守护,最稳定)node
docker·容器·node
我爱学习_zwj11 天前
Node.js:从浏览器到服务器的JS革命
javascript·node
276695829213 天前
闪购商家端 mtgsig
java·python·c#·node·c·mtgsig·mtgsig1.2
a31582380617 天前
Linux部署Python Django工程和Node工程,使用宝塔面板
linux·服务器·python·django·node·strapi·宝塔面板
武子康19 天前
AI研究-133 Java vs Kotlin/Go/Rust/Python/Node:2025 详细对比分析 定位与取舍指南
java·javascript·python·golang·rust·kotlin·node
276695829219 天前
最新 _rand 分析
前端·javascript·数据库·node·rand·231滑块·_rand分析
achi01022 天前
Ubuntu 24.04 LTS 下 Vue 3 开发环境搭建与生产部署完整指南
nginx·node·nvm·vue 3·ubuntu 24·开发环境搭建·国内镜像仓库
猿小猴子1 个月前
Node 的版本管理工具 nvm 介绍
npm·node·nvm
27669582921 个月前
朴朴超市小程序分析
java·python·小程序·node·sign·朴朴超市·sign-v2