当前位置: 首页 > news >正文

切片不够技术来凑

概述

随着数据经度的提升,18级的切片有些场景已经不够用了,但是大部分在线的栅格切片最大级别还是18级,如果地图继续放大,有的框架(leaflet会,openlayers和mapboxGL不会)会存在没有底图的情况。为处理这种情况,本文通过node实现在级别大于18级的时候将18级的切片进行裁切,解决没有底图的问题。

实现效果

动画.gif

实现代码

获取切片图片,如果z大于18,则取18级的切片进行切割;否则直接返回。

getTileData(z, x, y) {return new Promise(resolve => {let url = '', extent = [], xy18 = []if(z > 18 ) {extent = this.getTileExtent(z, x, y)const [minX, minY, maxX, maxY] = extent// 获取18级对应的索引xy18 = this.getTileIndexByCoords((minX + maxX) / 2, (minY + maxY) / 2)const [x18, y18] = xy18url = `https://webrd01.is.autonavi.com/appmaptile?style=8&lang=zh_cn&size=1&scale=1&x=${x18}&y=${y18}&z=18`} else {url = `https://webrd01.is.autonavi.com/appmaptile?style=8&lang=zh_cn&size=1&scale=1&x=${x}&y=${y}&z=${z}`}loadImage(url).then(image => {this.ctx.clearRect(0, 0, this.TILE_SIZE, this.TILE_SIZE)if(z > 18) {const [minX, minY, maxX, maxY] = extentconst [x18, y18] = xy18const [minX18, minY18, maxX18, maxY18] = this.getTileExtent(18, x18, y18)const [srcx18, srcy18] = this.toScreen(minX18, maxY18)const [srcxmin, srcymin] = this.toScreen(minX, maxY)const [srcxmax, srcymax] = this.toScreen(maxX, minY)const scrx = Math.round(srcxmin - srcx18), scry = Math.round(srcymin - srcy18)const width = Math.round(srcxmax - srcx18 - scrx), height = Math.round(srcymax - srcy18 - scry)this.ctx.drawImage(image, scrx, scry, width, height, 0, 0, this.TILE_SIZE, this.TILE_SIZE)} else {this.ctx.drawImage(image, 0, 0, this.TILE_SIZE, this.TILE_SIZE)}resolve(this.canvas.toBuffer('image/png'))})})
}

getTileExtent为根据切片索引获取切片范围,其实现如下:

getResolution(z) {return (this.TILE_ORIGIN * 2) / (Math.pow(2, z) * this.TILE_SIZE)
}
/*** 获取切片范围* @param {number} z * @param {number} x * @param {number} y * @returns {number}*/
getTileExtent(z, x, y) {const res = this.getResolution(z)const minX = x * this.TILE_SIZE * res - this.TILE_ORIGINconst maxX = (x + 1) * this.TILE_SIZE * res - this.TILE_ORIGINconst minY = this.TILE_ORIGIN - (y + 1) * this.TILE_SIZE * resconst maxY = this.TILE_ORIGIN - y * this.TILE_SIZE * resreturn [minX, minY, maxX, maxY]
}

其中

  • TILE_SIZE,切片大小,值为256;
  • TILE_ORIGIN,切片原点,值为20037508.34;
    getTileIndexByCoords为根据坐标获取切片索引,实现代码如下:
getTileIndexByCoords(x, y) {const res18 = this.getResolution(18) * this.TILE_SIZEreturn [Math.floor((x + this.TILE_ORIGIN) / res18),Math.floor((this.TILE_ORIGIN - y) / res18)]
}

toScreen实现将地理坐标转换为屏幕坐标。

toScreen(x, y) {const res18 = this.getResolution(18)return [(x + this.TILE_ORIGIN) / res18,(this.TILE_ORIGIN - y) / res18]
}

完整代码如下:

import { createCanvas, loadImage } from 'canvas'
import express from 'express'console.time('app')const app = express()// 自定义跨域中间件
const allowCors = function (req, res, next) {res.header('Access-Control-Allow-Origin', req.headers.origin);res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');res.header('Access-Control-Allow-Headers', 'Content-Type');res.header('Access-Control-Allow-Credentials', 'true');next();
};
app.use(allowCors);// 使用跨域中间件app.use(express.static('public'))class TileUtil {constructor() { this.TILE_ORIGIN = 20037508.34 // 切片原点this.TILE_SIZE = 256; // 切片大小this.canvas = createCanvas(this.TILE_SIZE, this.TILE_SIZE)this.ctx = this.canvas.getContext('2d')}/*** 计算分辨率* @param {number} z - 缩放级别* @returns {number}*/getResolution(z) {return (this.TILE_ORIGIN * 2) / (Math.pow(2, z) * this.TILE_SIZE)}/*** 获取切片范围* @param {number} z * @param {number} x * @param {number} y * @returns {number}*/getTileExtent(z, x, y) {const res = this.getResolution(z)const minX = x * this.TILE_SIZE * res - this.TILE_ORIGINconst maxX = (x + 1) * this.TILE_SIZE * res - this.TILE_ORIGINconst minY = this.TILE_ORIGIN - (y + 1) * this.TILE_SIZE * resconst maxY = this.TILE_ORIGIN - y * this.TILE_SIZE * resreturn [minX, minY, maxX, maxY]}/*** 将地理坐标转换为屏幕坐标* @param {number} x * @param {number} y * @returns {number}*/toScreen(x, y) {const res18 = this.getResolution(18)return [(x + this.TILE_ORIGIN) / res18,(this.TILE_ORIGIN - y) / res18]}/*** 获取切片图片,如果z大于18,则取18级的切片进行切割;否则直接返回* @param {number} z * @param {number} x * @param {number} y * @returns {Buffer<Image>}*/getTileData(z, x, y) {return new Promise(resolve => {let url = '', extent = [], xy18 = []if(z > 18 ) {extent = this.getTileExtent(z, x, y)const [minX, minY, maxX, maxY] = extent// 获取18级对应的索引xy18 = this.getTileIndexByCoords((minX + maxX) / 2, (minY + maxY) / 2)const [x18, y18] = xy18url = `https://webrd01.is.autonavi.com/appmaptile?style=8&lang=zh_cn&size=1&scale=1&x=${x18}&y=${y18}&z=18`} else {url = `https://webrd01.is.autonavi.com/appmaptile?style=8&lang=zh_cn&size=1&scale=1&x=${x}&y=${y}&z=${z}`}loadImage(url).then(image => {this.ctx.clearRect(0, 0, this.TILE_SIZE, this.TILE_SIZE)if(z > 18) {const [minX, minY, maxX, maxY] = extentconst [x18, y18] = xy18const [minX18, minY18, maxX18, maxY18] = this.getTileExtent(18, x18, y18)const [srcx18, srcy18] = this.toScreen(minX18, maxY18)const [srcxmin, srcymin] = this.toScreen(minX, maxY)const [srcxmax, srcymax] = this.toScreen(maxX, minY)const scrx = Math.round(srcxmin - srcx18), scry = Math.round(srcymin - srcy18)const width = Math.round(srcxmax - srcx18 - scrx), height = Math.round(srcymax - srcy18 - scry)this.ctx.drawImage(image, scrx, scry, width, height, 0, 0, this.TILE_SIZE, this.TILE_SIZE)} else {this.ctx.drawImage(image, 0, 0, this.TILE_SIZE, this.TILE_SIZE)}resolve(this.canvas.toBuffer('image/png'))})})}/*** 根据坐标获取切片索引* @param {number} x * @param {number} y * @returns {[<number>, <number>]}*/getTileIndexByCoords(x, y) {const res18 = this.getResolution(18) * this.TILE_SIZEreturn [Math.floor((x + this.TILE_ORIGIN) / res18),Math.floor((this.TILE_ORIGIN - y) / res18)]}
}const util = new TileUtil()app.get('/tile/:z/:x/:y', (req, res) => {const { z, x, y } = req.paramsutil.getTileData(Number(z), Number(x), Number(y)).then(data => {res.setHeader('Expires', new Date(Date.now() + 30 * 1000).toUTCString())res.writeHead(200, {"Content-Type": "image/png",});res.end(data);})
})app.get('/tile-bbox/:z/:x/:y', (req, res) => {const { z, x, y } = req.paramsconst TILE_SIZE = 256;const canvas = createCanvas(TILE_SIZE, TILE_SIZE)const ctx = canvas.getContext('2d')ctx.fillStyle = '#f00'ctx.strokeStyle = '#f00'ctx.lineWidth = 2ctx.textAlign = "center";ctx.textBaseline = "middle"ctx.font = "bold 18px 微软雅黑";ctx.strokeRect(0, 0, TILE_SIZE, TILE_SIZE)ctx.fillText(`${z}-${x}-${y}`, TILE_SIZE / 2, TILE_SIZE / 2)res.setHeader('Expires', new Date(Date.now() + 30 * 1000).toUTCString())res.writeHead(200, {"Content-Type": "image/png",});res.end(canvas.toBuffer('image/png'));
})app.listen(18089, () => {console.timeEnd('app')console.log('express server running at http://127.0.0.1:18089')
})

相关文章:

切片不够技术来凑

概述 随着数据经度的提升&#xff0c;18级的切片有些场景已经不够用了&#xff0c;但是大部分在线的栅格切片最大级别还是18级&#xff0c;如果地图继续放大&#xff0c;有的框架&#xff08;leaflet会&#xff0c;openlayers和mapboxGL不会&#xff09;会存在没有底图的情况。…...

特约|数码转型思考:Web3.0与银行

日前&#xff0c;欧科云链研究院发布重磅报告&#xff0c;引发银行界及金融监管机构广泛关注。通过拆解全球70余家银行的加密布局&#xff0c;报告认为&#xff0c;随着全球采用率的提升与相关技术的成熟&#xff0c;加密资产已成为银行业不容忽视也不能错过的创新领域。 作为…...

MySQL知识详细汇总

存储引擎 MyISAM 不支持事务&#xff0c;不支持外键&#xff0c;支持全文索引&#xff0c;查询、插入效率高InnoDB 支持事务&#xff08;事务的特性&#xff09; 原子性&#xff1a;一个事务中所有的操作&#xff0c;要么全部完成&#xff0c;要么全部不完成&#xff0c;不会在…...

【驱动开发】LED灯的亮灭——通过字符设备驱动的分步实现编写LED驱动,实现设备文件和设备的绑定

头文件&#xff1a; #ifndef __HEAD_H__ #define __HEAD_H__typedef struct {unsigned int MODER;unsigned int OTYPER;unsigned int OSPEEDR;unsigned int PUPDR;unsigned int IDR;unsigned int ODR; }gpio_t;//LED灯的寄存器地址 #define LED1_ADDR 0X50006000 #define L…...

华为OD 最小数字(100分)【java】A卷+B卷

华为OD统一考试A卷+B卷 新题库说明 你收到的链接上面会标注A卷还是B卷。目前大部分收到的都是B卷。 B卷对应20022部分考题以及新出的题目,A卷对应的是新出的题目。 我将持续更新最新题目 获取更多免费题目可前往夸克网盘下载,请点击以下链接进入: 我用夸克网盘分享了「华为O…...

大模型、实时需求推动湖仓平台走向开放

大模型、实时需求高涨 AGI 时代&#xff0c;以 ChatGPT、Midjourney 等为代表的大模型迅速应用加速了 AI 普及&#xff0c;越来越多的企业选择搭建自己的 AI 基础设施&#xff0c;训练行业大模型。 另一方面&#xff0c;企业为了在瞬息万变的市场环境中更快的做出商业决策&…...

Linux搭建文件服务器

搭建简单文件服务器 基于centos7.9搭建http文件服务器基于centos7.9搭建nginx文件服务器基于ubuntu2204搭建http文件服务器 IP环境192.168.200.100VMware17 基于centos7.9搭建http文件服务器 安装httpd [rootlocalhost ~]# yum install -y httpd关闭防火墙以及selinux [roo…...

跨境商城源码可以支持多种支付方式吗?

跨境商城源码是一种用于建立跨国界电商平台的程序代码。随着全球电商的繁荣发展&#xff0c;越来越多的商家开始寻找一种既安全可靠&#xff0c;又能满足用户需求的支付方式。那么&#xff0c;跨境商城源码是否能够支持多种支付方式呢?让我们深入探讨一下。 1. 支付宝支付 支付…...

机器学习中的核方法

一、说明 线性模型很棒&#xff0c;因为它们易于理解且易于优化。他们受苦是因为他们只能学习非常简单的决策边界。神经网络可以学习更复杂的决策边界&#xff0c;但失去了线性模型良好的凸性特性。 使线性模型表现出非线性的一种方法是转换输入。例如&#xff0c;通过添加特征…...

搜索问答技术学习:基于知识图谱+基于搜索和机器阅读理解(MRC)

目录 一、问答系统应用分析 二、搜索问答技术与系统 &#xff08;一&#xff09;需求和信息分析 问答需求类型 多样的数据源 文本组织形态 &#xff08;二&#xff09;主要问答技术介绍 发展和成熟度分析 重点问答技术基础&#xff1a;KBQA和DeepQA KBQA&#xff08;…...

LeetCode2409——统计共同度过的日子数

博主的解法过于冗长&#xff0c;是一直对着不同的案例debug修改出来的&#xff0c;不建议学习。虽然提交成功了&#xff0c;但是自己最后都不知道写的是啥了哈哈哈。 package keepcoding.leetcode.leetcode2409; /*Alice 和 Bob 计划分别去罗马开会。给你四个字符串 arriveA…...

【MyBatisPlus】快速入门、常用注解、常用配置

&#x1f40c;个人主页&#xff1a; &#x1f40c; 叶落闲庭 &#x1f4a8;我的专栏&#xff1a;&#x1f4a8; c语言 数据结构 javaEE 操作系统 Redis 石可破也&#xff0c;而不可夺坚&#xff1b;丹可磨也&#xff0c;而不可夺赤。 MyBatisPlus 一、快速入门1.1 引入MyBatisP…...

【USRP】通信之:光通信

光通信: 光通信是使用光信号&#xff08;通常是红外或可见光信号&#xff09;在光纤或空气中传输信息的技术。由于光信号的特性&#xff0c;光通信具有非常高的数据传输率和长距离传输能力。以下是光通信的一些关键组件和概念&#xff1a; 光纤&#xff1a; 是由非常纯净的玻璃…...

bpf对内核的观测

目录 1 bpftrace常用命令1.1 列出bpftrace 相关命令的list1. 2bpftrace -e 是执行1.3 查看参数 -lv 2 bpftrace 可以用到的变量3 高级3.1 内置函数3.2 文件系统3.3 内核内存 栈3.4 Malloc 调用 统计3.5 系统调用 brk 的 统计3.6 脚本调用 4 应用5 怎么串联起来呢 bpftrace 总的…...

Tiktok shop api 调试

记录一下调试Tiktok shop api 踩坑记录。 主要是在按官网api上规则和加密生成sign时候一直通不过的问题&#xff1a; 官网地址&#xff1a;https://partner.tiktokshop.com/doc/page/63fd743e715d622a338c4eab 直接贴代码了 import lombok.extern.slf4j.Slf4j;import javax.cr…...

QFSFileEngine::open: No file name specified解决方案

问题 使用QFile类进行文件操作时&#xff0c;报错QFSFileEngine::open: No file name specified。 原因 QFile::open: No file name specified是Qt中的一个错误消息&#xff0c;提示没有指定文件名导致文件无法打开。这个错误通常出现在使用QFile::open()函数时没有提供有效…...

Flappy bird项目

一、功能分析 1、小鸟自动向右滑行 2、按下空格小鸟上升&#xff0c;不按下落 3、显示小鸟需要穿过的管道 4、管道自动左移和创建 5、小鸟和管道碰撞&#xff0c;游戏结束 6、技术 7、 项目框图 8、Ncurses 1&#xff09;创建窗口界面&#xff0c;移动光标&#xff0c;产…...

高校教务系统登录页面JS分析——西安科技大学

高校教务系统密码加密逻辑及JS逆向 本文将介绍高校教务系统的密码加密逻辑以及使用JavaScript进行逆向分析的过程。通过本文&#xff0c;你将了解到密码加密的基本概念、常用加密算法以及如何通过逆向分析来破解密码。 本文仅供交流学习&#xff0c;勿用于非法用途。 一、密码加…...

Mysql 事务的实现原理

Mysql 里面的事务&#xff0c;满足 ACID 特性&#xff0c;所以Mysql 的事务实现原理&#xff0c;就是InnoDB 是如何保证 ACID 特性的。 ACID A 表示 Atomic 原子性&#xff0c;也就是需要保证多个 DML 操作是原子的&#xff0c;要么都成功&#xff0c;要么都失败。那么&#xf…...

使用vscode搭建虚拟机

首先vscode插件安装 名称: Remote - SSH ID: ms-vscode-remote.remote-ssh 说明: Open any folder on a remote machine using SSH and take advantage of VS Codes full feature set. 版本: 0.51.0 VS Marketplace 链接: https://marketplace.visualstudio.com/items?it…...

JavaSec-RCE

简介 RCE(Remote Code Execution)&#xff0c;可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景&#xff1a;Groovy代码注入 Groovy是一种基于JVM的动态语言&#xff0c;语法简洁&#xff0c;支持闭包、动态类型和Java互操作性&#xff0c…...

云计算——弹性云计算器(ECS)

弹性云服务器&#xff1a;ECS 概述 云计算重构了ICT系统&#xff0c;云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台&#xff0c;包含如下主要概念。 ECS&#xff08;Elastic Cloud Server&#xff09;&#xff1a;即弹性云服务器&#xff0c;是云计算…...

汇编常见指令

汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX&#xff08;不访问内存&#xff09;XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)

文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案

JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停​​ 1. ​​安全点(Safepoint)阻塞​​ ​​现象​​:JVM暂停但无GC日志,日志显示No GCs detected。​​原因​​:JVM等待所有线程进入安全点(如…...

论文笔记——相干体技术在裂缝预测中的应用研究

目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术&#xff1a;基于互相关的相干体技术&#xff08;Correlation&#xff09;第二代相干体技术&#xff1a;基于相似的相干体技术&#xff08;Semblance&#xff09;基于多道相似的相干体…...

QT3D学习笔记——圆台、圆锥

类名作用Qt3DWindow3D渲染窗口容器QEntity场景中的实体&#xff08;对象或容器&#xff09;QCamera控制观察视角QPointLight点光源QConeMesh圆锥几何网格QTransform控制实体的位置/旋转/缩放QPhongMaterialPhong光照材质&#xff08;定义颜色、反光等&#xff09;QFirstPersonC…...

多模态图像修复系统:基于深度学习的图片修复实现

多模态图像修复系统:基于深度学习的图片修复实现 1. 系统概述 本系统使用多模态大模型(Stable Diffusion Inpainting)实现图像修复功能,结合文本描述和图片输入,对指定区域进行内容修复。系统包含完整的数据处理、模型训练、推理部署流程。 import torch import numpy …...

从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障

关键领域软件测试的"安全密码"&#xff1a;Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力&#xff0c;从金融交易到交通管控&#xff0c;这些关乎国计民生的关键领域…...

「全栈技术解析」推客小程序系统开发:从架构设计到裂变增长的完整解决方案

在移动互联网营销竞争白热化的当下&#xff0c;推客小程序系统凭借其裂变传播、精准营销等特性&#xff0c;成为企业抢占市场的利器。本文将深度解析推客小程序系统开发的核心技术与实现路径&#xff0c;助力开发者打造具有市场竞争力的营销工具。​ 一、系统核心功能架构&…...