【MBtiles数据索引和服务发布】GeoServer改造Springboot番外系列二
xyz地图服务访问示例:http://192.168.1.240:8081/gmserver/raster/xyz/firstWP:Imagery-raster/{z}/{x}/{y}.jpg
访问示例如下:

mbtiles目录结构
根据z,x,y获取对应mbtiles文件路径的工具方法
说明:重点是使用getMbtilesPath方法,通过xyz获取mbtiles文件路径。
getTilesFile方法是通过图层(因为我做的项目是mbtiles数据集绑定在图层上)获取对应的mbtiles文件。
package com.gs.springboot.gmserver.util;import com.gs.springboot.gmserver.core.CommonConstants;
import org.geoserver.catalog.LayerInfo;
import org.geoserver.catalog.StoreInfo;import java.io.File;
import java.io.Serializable;
import java.util.Collection;
import java.util.Map;/*** @Desc mbtiles工具类*/
public class MbtilesUtil {/*** 根据zxy索引mbtiles文件所在目录** @param z 层级* @param x 行* @param y 列* @param rootPath mbtiles根目录* @return java.lang.String*/public static String getMbtilesPath(int z, int x, int y, String rootPath) {int p = 0;//文件级别的列号int q = 0;//文件级别的行号int m = 0;//文件夹级别的列号int n = 0;//文件夹级别的行号if (z <= 8) {p = 0;q = 0;m = 0;n = 0;} else if (z >= 9) {double fileTotal = Math.pow(2, 2 * (z - 8));//文件总数double tileTotal = 65536 * fileTotal;//瓦片总数double maxTileNum = Math.sqrt(tileTotal);//行和列坐标轴方向的瓦片个数,行列相等double segmentNum = Math.sqrt(fileTotal);//行和列分的段数,即行和列坐标轴方向的mbtiles文件个数double tileNumOfEachMbtiles = maxTileNum / segmentNum;//单个mbtiles的最大行列的瓦片数,行列瓦片数相等p = (int) (x / tileNumOfEachMbtiles);q = (int) (y / tileNumOfEachMbtiles);if (z == 9 || z == 10) {m = 0;n = 0;} else {double dirTotal = fileTotal / 16;//文件夹总数double segmentDirNum = Math.sqrt(dirTotal);//行和列坐标轴方向的最大文件夹个数,行列相等double tileNumOfEachDir = segmentNum / segmentDirNum;//每段文件夹内的mbtiles的个数m = (int) (p / tileNumOfEachDir);n = (int) (q / tileNumOfEachDir);}}String mbtiles_file = rootPath + "/" + z + "/" + m + "_" + n + "/" + z + "_" + p + "_" + q + ".mbtiles";return mbtiles_file;}/*** 通过图层信息索引瓦片对应文件* @param layerInfo 图层* @param tilecol 列x* @param tilerow 行y* @param z 层级z* @param format 后缀* @return java.io.File*/public static File getTilesFile(LayerInfo layerInfo, int tilecol, int tilerow, Integer z, String format) {StoreInfo store = layerInfo.getResource().getStore();String type = store.getType();File file = null;Map<String, Serializable> connectionParameters = store.getConnectionParameters();for (String key : connectionParameters.keySet()) {if (key.contains("filePath")) {Serializable value = connectionParameters.get(key);String rootPath = (String) value;String tilesPath = null;if (type.equals(CommonConstants.DataStoreType.TilesFolderRaster.getValue())|| type.equals(CommonConstants.DataStoreType.TilesFolderVector.getValue())|| type.equals(CommonConstants.DataStoreType.TilesFolderDEM_terrain.getValue())) {tilesPath = rootPath + "/" + z + "/" + tilecol + "/" + tilerow + "." + format;} else if (type.equals(CommonConstants.DataStoreType.TilesFolderDEM_png.getValue())) {tilesPath = rootPath + "/" + z + "/" + tilecol + "/" + tilerow + ".png";}else if (type.equals(CommonConstants.DataStoreType.MbtilesFolderRaster.getValue())|| type.equals(CommonConstants.DataStoreType.MbtilesFolderVector.getValue())|| type.equals(CommonConstants.DataStoreType.MbtilesFolderDEM.getValue())) {tilesPath = getMbtilesPath(z, tilecol, tilerow, rootPath);}file = new File(tilesPath);if (file.exists() && file.length() > 0) {break;}}}return file;}}
发布mbtiles地图服务的接口。
说明:此处由于是项目的完整功能,所以代码是通过图层名称获取mbtiles的文件,你也可以将layer直接换成mbtiles数据集的根目录,或者直接写死根目录。通过xyz就可以访问瓦片。
package com.gs.springboot.gmserver.tiles;import cn.hutool.core.io.file.FileNameUtil;
import com.gs.springboot.gmserver.core.CommonConstants;
import com.gs.springboot.gmserver.util.MbtilesUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.io.IOUtils;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.LayerInfo;
import org.geoserver.catalog.StoreInfo;
import org.imintel.mbtiles4j.MBTilesReadException;
import org.imintel.mbtiles4j.MBTilesReader;
import org.imintel.mbtiles4j.Tile;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.concurrent.CompletableFuture;@RestController
@Api(tags = "栅格瓦片服务发布接口")
public class RasterTilesController {@Autowiredprivate Catalog catalog;@GetMapping("/raster/xyz/{layer}/{z}/{x}/{y}.{format}")@ApiOperation(value = "发布栅格Mbtiles文件夹数据XYZ服务")@ApiImplicitParams({@ApiImplicitParam(name = "layer", value = "图层名称", dataType = "String", paramType = "path"),@ApiImplicitParam(name = "z", value = "z坐标", dataType = "int", paramType = "path"),@ApiImplicitParam(name = "x", value = "x坐标", dataType = "int", paramType = "path"),@ApiImplicitParam(name = "y", value = "y坐标", dataType = "int", paramType = "path"),@ApiImplicitParam(name = "format", value = "格式", dataType = "String", paramType = "path")})public void publishXYZ(@PathVariable("layer") String layer,@PathVariable("z") int z,@PathVariable("x") int x,@PathVariable("y") int y,@PathVariable("format") String format,HttpServletResponse response) {LayerInfo layerInfo = catalog.getLayerByName(layer);StoreInfo store = layerInfo.getResource().getStore();if (store.getConnectionParameters().get("scheme") != null) {String scheme = (String) store.getConnectionParameters().get("scheme");if (!CommonConstants.DataStoreScheme.XYZ.getValue().equals(scheme)) {y = (1 << z) - y - 1;//此处是将xyz转tms}}extracted(layerInfo, z, x, y, format, response);}private void extracted(LayerInfo layerInfo, int z, int x, int y, String format, HttpServletResponse response) {File file = MbtilesUtil.getTilesFile(layerInfo, x, y, z, format);String prefix = FileNameUtil.getSuffix(file);// 异步执行任务,返回一个 CompletableFutureCompletableFuture<Void> future = CompletableFuture.runAsync(() -> {InputStream data = null;if ("mbtiles".equals(prefix)) {MBTilesReader r = null;Tile tile;try {r = new MBTilesReader(file);tile = r.getTile(z, x, y);} catch (MBTilesReadException e) {throw new RuntimeException(e);}data = tile.getData();} try {if ("png".equals(format) || "jpg".equals(format)) {response.setContentType("image/" + format);} else {response.setStatus(400);return;}ServletOutputStream oStream = response.getOutputStream();IOUtils.copy(data, oStream);oStream.flush();oStream.close();} catch (IOException e) {throw new RuntimeException(e);}});// 等待 CompletableFuture 完成future.join();}}
相关文章:
【MBtiles数据索引和服务发布】GeoServer改造Springboot番外系列二
xyz地图服务访问示例:http://192.168.1.240:8081/gmserver/raster/xyz/firstWP:Imagery-raster/{z}/{x}/{y}.jpg 访问示例如下: mbtiles目录结构 根据z,x,y获取对应mbtiles文件路径的工具方法 说明:重点是使用getMb…...
Redis抓取数据到Logstash再推到Elasticsearch集群
一、安装Logstash 前面安装过Logstash了,不做解释直接跳过 参考:上一篇文章 二、配置Logstash 在logstash目录下,编辑我们之前的配置文件logstash.conf vim logstash.confinput、output字面意思,从redis去拿取数据,输出到Elasticsearch data_type:数据类型为list k…...
【代码随想录-链表】反转链表
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学习,不断总结,共同进步,活到老学到老导航 檀越剑指大厂系列:全面总结 jav…...
32GPIO输入LED闪烁蜂鸣器
一.GPIO简介 所有的GPIO都挂载到APB2上,每个GPIO有16个引脚 内核可以通过APB2对寄存器进行读写,寄存器都是32位的,但每个引脚端口只有16位 驱动器用于增加信号的驱动能力 二.具体…...
Qt|QPushButton控件讲解
前提 按钮分为了四种状态:常态、聚焦、按下、禁用 前一段时间更新了MFC框架下CButton的自绘。因为MFC框架下的按钮限制性很高,所以只能由自绘实现各种风格,但是QT框架完美的解决了这个问题,我们只需要了解如何调用,就…...
再学webpack
1 优化 webpack 打包体积的思路 优化 webpack 打包体积的思路包括: 提取第三方库或通过引用外部文件的方式引入第三方库:将第三方库单独打包,并通过 CDN 引入,减少打包体积。使用代码压缩插件:例如 UglifyJsPlugin&a…...
systemd:service与target使用及相关命令
文章目录 一、 unit1.1 unit常用命令 二、 service系统服务2.1 unit service配置文件2.1.1 [Unit]区块2.1.2 [Service]区块2.1.3 [Install]区块2.1.4 示例介绍 2.2 service常用命令 三、target3.1 tartget有关命令 四、其他系统命令4.1 systemctl 相关系统管理命令4.2 journalc…...
FairGuard游戏加固入选《CCSIP 2023中国网络安全行业全景册(第六版)》
2024年1月24日, FreeBuf咨询正式发布《CCSIP 2023中国网络安全行业全景册(第六版)》。本次发布的全景图,共计展示20个一级分类、108个细分安全领域,旨在为广大企业提供网络安全产品选型参考,帮助企业了解中国网络安全技术与市场的…...
文心一言 VS ChatGPT :谁是更好的选择?
前言 目前各种大模型、人工智能相关内容覆盖了朋友圈已经各种媒体平台,对于Ai目前来看只能说各有千秋。GPT的算法迭代是最先进的,但是它毕竟属于国外产品,有着网络限制、注册限制、会员费高昂等弊端,难以让国内用户享受。文心一言…...
七街八巷×实在RPA丨财务凭证录入零出错,效率提升8倍
在如今的数字化时代,企业财务面临着海量且复杂的数据流程。特别是在凭证录入这一看似简单却又频繁的环节中,传统的手工操作已无法满足日益增长的业务需求。 中国轻食知名品牌七街八巷,通过部署实在智能集“自动化平台开箱即用解决方案咨询”…...
线性代数----------学习记录
线性代数发展历程 (1)线性方程组:例如二元一次方程组; (2)行列式:determinant,克莱默,莱布尼兹; (3)矩阵:方程个数与未知数的个数可…...
Ubuntu如何安装使用Nginx反向代理?
在Ubuntu上安装Nginx并配置使其生效是相对简单的过程。以下是一步一步的指南: 步骤 1:安装 Nginx 打开终端,并执行以下命令来安装 Nginx: sudo apt update sudo apt install nginx步骤 2:启动 Nginx 服务 安装完成…...
Linux系统——正则表达式
有一段时间本机访问量过高,如何查看日志提取出访问量前十的信息 1.使用提取命令(cut、awk、sed)提取出ip地址的那一列 2.使用sort按数字排序,将相同的地址整合到一起 3.使用uniq -c统计出数量 4.使用sort 数字 数字倒序排序 5.最…...
【文本到上下文 #9】NLP中的BERT和迁移学习
一、说明 BERT:适合所有人的架构概述:我们将分解 BERT 的核心组件,解释该模型如何改变机器理解人类语言的方式,以及为什么它比以前的模型有重大进步。 BERT的变体: 在BERT取得成功之后,已…...
github单文件下载——DownGit
记录一下一个好用的网站,支持github中某一特定文件夹下文件的下载 选择一个合适的梯子,访问Downgit网址 https://minhaskamal.github.io/DownGit/#/home 将所需网站连接粘贴到这里,点击download即可...
Java之Stream的应用与原理分析
前言 stream是怎么做到一次迭代中将所有流操作进行叠加?stream怎么做到只有在终止操作时进行元素遍历?那中间操作是做了些什么? Stream 与集合的区别 集合是内存中的数据结构抽象,描述了数据在内存中是如何存储的。流描述了对数据…...
Git 实战场景过程(工作总结篇)
目录 前言1. Git远程仓库建立分支,本地未显示1.1 问题所示1.2 知识补充 2. Git暂存内容切换分支2.1 问题所示2.2 知识补充 3. Git放弃修改数据3.1 问题所示3.2 知识补充 前言 主要总结工作中的疑惑点,如果你也有相应的场景,可以评论区见&…...
iperf3 打流工具的使用介绍
目录 1. iperf3简介 2. 详细命令参数 1. iperf3简介 iPerf3是用于主动测试IP网络上最大可用带宽的工具。它支持时序、缓冲区、协议(TCP,UDP,SCTP与IPv4和IPv6)有关的各种参数。对于每次测试,它都会详细的带宽报告&a…...
网络异常案例一_RST
本文以及后面几篇会整理输出下以前处理过的一些网络相关的异常 4G定向卡上网问题 问题现象,自研路由器,使用运营商定向的4G卡上网,访问服务器异常,相应的开发同学反馈被服务器拒绝了; 复现问题,同步在cli…...
提升工作效率,畅享便捷PDF编辑体验——Adobe Acrobat Pro DC 2023
作为全球领先的PDF编辑软件,Adobe Acrobat Pro DC 2023将为您带来前所未有的PDF编辑体验。无论您是个人用户还是企业用户,Adobe Acrobat Pro DC 2023将成为您提高工作效率、简化工作流程的得力助手。 一、全面编辑功能 Adobe Acrobat Pro DC 2023提供了…...
通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...
ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
云原生安全实战:API网关Kong的鉴权与限流详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关(API Gateway) API网关是微服务架构中的核心组件,负责统一管理所有API的流量入口。它像一座…...
c++第七天 继承与派生2
这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分:派生类构造函数与析构函数 当创建一个派生类对象时,基类成员是如何初始化的? 1.当派生类对象创建的时候,基类成员的初始化顺序 …...
Leetcode33( 搜索旋转排序数组)
题目表述 整数数组 nums 按升序排列,数组中的值 互不相同 。 在传递给函数之前,nums 在预先未知的某个下标 k(0 < k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...
Java 与 MySQL 性能优化:MySQL 慢 SQL 诊断与分析方法详解
文章目录 一、开启慢查询日志,定位耗时SQL1.1 查看慢查询日志是否开启1.2 临时开启慢查询日志1.3 永久开启慢查询日志1.4 分析慢查询日志 二、使用EXPLAIN分析SQL执行计划2.1 EXPLAIN的基本使用2.2 EXPLAIN分析案例2.3 根据EXPLAIN结果优化SQL 三、使用SHOW PROFILE…...
如何做好一份技术文档?从规划到实践的完整指南
如何做好一份技术文档?从规划到实践的完整指南 🌟 嗨,我是IRpickstars! 🌌 总有一行代码,能点亮万千星辰。 🔍 在技术的宇宙中,我愿做永不停歇的探索者。 ✨ 用代码丈量世界&…...
python读取SQLite表个并生成pdf文件
代码用于创建含50列的SQLite数据库并插入500行随机浮点数据,随后读取数据,通过ReportLab生成横向PDF表格,包含格式化(两位小数)及表头、网格线等美观样式。 # 导入所需库 import sqlite3 # 用于操作…...
2025 后端自学UNIAPP【项目实战:旅游项目】7、景点详情页面【完结】
1、获取景点详情的请求【my_api.js】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http(/login/getWXSessionKey, {code,avatar}); };//…...
旋量理论:刚体运动的几何描述与机器人应用
旋量理论为描述刚体在三维空间中的运动提供了强大而优雅的数学框架。与传统的欧拉角或方向余弦矩阵相比,旋量理论通过螺旋运动的概念统一了旋转和平移,在机器人学、计算机图形学和多体动力学领域具有显著优势。这种描述不仅几何直观,而且计算…...
