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

概述

本文介绍基于geojson-vt和canvas,实现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}'
  })
}),
相关推荐
张某布响丸辣20 天前
Node 16 下用 pdfjs + @napi-rs/canvas 把中文 PDF 渲染成图片
pdf·node·字体映射
DreamLife☼23 天前
Agent开发环境搭建完全指南
python·docker·typescript·node·工业知识点
大牧师24 天前
MySQL 学习教程
数据库·sql·mysql·docker·node·全栈·后端数据
Json____1 个月前
基于 Node + Vue3 的选课管理系统:从教务业务到全栈实践
前端·node·毕设·wwwoop.com
Json____1 个月前
校园跑腿系统:使用node从零构建一套前后端分离的全栈实践项目
计算机网络·node·管理系统·wwwoop.com
tangyal1 个月前
kubernetes(k8s)基础理论与集群部署
kubernetes·k8s·部署·node·理论·pod·集群cluster
北漂燕郊杨哥1 个月前
npm 缓存占满 C 盘?三步把它迁到 D 盘(附验证与避坑)
缓存·npm·node
狗凯之家源码网2 个月前
机车圈系统核心功能与实战效果展示
vue·node
特立独行的猫a3 个月前
HarmonyOS鸿蒙PC三方库移植:Node.js 三方库鸿蒙 PC 移植指南
华为·node.js·node·harmonyos·三方库移植·鸿蒙pc
daqinzl4 个月前
Mpegts.js+FFmpeg+WebSocket+Node实时视频流实现方案
websocket·ffmpeg·node·mpegts.js