【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 {

    @Autowired
    private 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);
        // 异步执行任务,返回一个 CompletableFuture
        CompletableFuture<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();
    }


}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/361663.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

STM32——I2C

通信协议见&#xff08;STM32——SPI&#xff09; 一、I2C协议 1.1 I2C协议介绍&#xff1b; I2C是&#xff08;Inter IC Bus&#xff09;是由Philips公司开发的一种通用数据总线&#xff1b; 有多根通信线&#xff1b; 一根SDA&#xff08;串行通信线&#xff09;&#xf…

PySpark(一)Spark原理介绍、PySpark初体验及原理

Spark简介 Apache Spark是用于大规模数据&#xff08;large-scala data&#xff09;处理的统一&#xff08;unified&#xff09;分析引擎&#xff0c;其特点就是对任意类型的数据进行自定义计算。 Spark VS Hadoop 尽管Spark相对于Hadoop而言具有较大优势&#xff0c;但Spark并…

Three.js 纹理贴图 - 环境贴图 - 纹理贴图 - 透明贴图 - 高光贴图

文章目录 Three.js 纹理贴图纹理贴图 map属性纹理贴图的映射方式 texture.Mapping纹理加载器 THREE.TextureLoader监听单个材质监听多个材质 - LoadingManager类 1. 颜色贴图与材质的颜色2.渲染效果&#xff1a;UV坐标 - 描述纹理贴图的坐标自定义顶点UVgeometry.attributes.uv…

Java 数据抓取

大家好我是苏麟 , 今天聊聊数据抓取 . 大家合理使用 注意&#xff0c;爬虫技术不能滥用&#xff0c;干万不要给别人的系统造成压力、不要侵犯他人权益! 数据抓取 实质上就是java程序模拟浏览器进行目标网站的访问&#xff0c;无论是请求目标服务器的接口还是请求目标网页内容…

python基础——池

池的介绍&#xff1a; 提前创建进程池&#xff0c;防止创建的进程数量过多导致系统性能受到影响&#xff0c;在系统执行任务时&#xff0c;系统会使用池中已经创建进程/线程&#xff0c;从而防止资源的浪费&#xff0c;创建的进程/线程可以让多个进程使用&#xff0c;从而降低…

Unity技术手册-UGUI零基础详细教程-Canvas详解

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 &#x1f449;关于作者 专注于Android/Unity和各种游…

备战蓝桥杯---数据结构与STL应用(入门4)

本专题主要是关于利用优先队列解决贪心选择上的“反悔”问题 话不多说&#xff0c;直接看题&#xff1a; 下面为分析&#xff1a; 很显然&#xff0c;我们在整体上以s[i]为基准&#xff0c;先把士兵按s[i]排好。然后&#xff0c;我们先求s[i]大的开始&#xff0c;即规定选人数…

电源模块欠压保护点测试方法分享 纳米软件

电源欠压保护原理 欠压保护是指当电源电压低于一定值时&#xff0c;电源的保护功能会及时断开电路&#xff0c;避免设备受到损坏。电源欠压保护一般是通过一个或多个传感器来检测电压&#xff0c;当电压低于设定值时就会触发电源的保护功能&#xff0c;断开电路&#xff0c;保护…

Git--07--GitExtension

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、GitExtension下载GitExtension官网下载教程 二、GitExtension安装三、GitExtension配置四、GitExtension使用 一、GitExtension下载 官网下载&#xff1a; http…

Docker中安装MySql的遇到的问题

目录 一、mysql查询中文乱码问题 1. 进入mysql中进行查看数据库字符集 2. 修改 my.cnf 中的配置 3. 重启mysql容器&#xff0c;使得容器重新加载配置文件 4. 测试结果 二、主从同步中遇到的问题 2.1 Slave_IO_Running:Connecting 的解决方案 1. 确定宿主机防火墙开放my…

嵌入式学习第十四天!(结构体、共用体、枚举、位运算)

1. 结构体&#xff1a; 1. 结构体类型定义&#xff1a; 嵌入式学习第十三天&#xff01;&#xff08;const指针、函数指针和指针函数、构造数据类型&#xff09;-CSDN博客 2. 结构体变量的定义&#xff1a; 嵌入式学习第十三天&#xff01;&#xff08;const指针、函数指针和…

推荐一个好用的旧版本软件安装包下载地址

最近要下载旧版本的mysql和postman&#xff0c;发现官网和其他博客里边提供的地址&#xff0c;要不都非常难找到相应的下载链接&#xff0c;要不就是提供的从别的地方复制过来的垃圾教程&#xff0c;甚至有的下载还要积分&#xff0c;反正是最后都没下载成功&#xff0c;偶然发…

安全通道堵塞识别摄像机

当建筑物的安全通道发生堵塞时&#xff0c;可能会给人员疏散和救援带来重大隐患。为了及时识别和解决安全通道堵塞问题&#xff0c;专门设计了安全通道堵塞识别摄像机&#xff0c;它具有监测、识别和报警功能&#xff0c;可在第一时间发现通道堵塞情况。这种摄像机通常安装在通…

Mysql-索引创建,索引失效案例

索引创建建议 1 什么情况下需要创建索引&#xff1f; 频繁出现在where 条件字段&#xff0c;order排序&#xff0c;group by分组字段select 频繁查询的列&#xff0c;考虑是否需要创建联合索引&#xff08;覆盖索引&#xff0c;不回表&#xff09;多表join关联查询&#xff0…

【PyRestTest】高级使用

本节主要涉及PyRestTest的高级特征的详细使用,主要指:generators(生成器), variable binding(变量绑定), data extraction(数据提取), content validators(文本验证) 它们是如何组合在一起的? 模板和上下文 测试和基准测试可以使用变量来模板化动态配置。使用基础的…

OSPF排错

目录 实验拓扑图 实验要求 实验排错 故障一 故障现象 故障分析 故障解决 故障二 故障现象 故障分析 故障解决 故障三 故障现象 故障分析 故障解决 故障四 故障现象 故障分析 故障解决 故障五 故障现象 故障分析 故障解决 故障六 故障现象 故障分析 …

区块链游戏解说:什么是 SecondLive

数据源&#xff1a;SecondLive Dashboard 作者&#xff1a;lesleyfootprint.network 什么是 SecondLive SecondLive 是元宇宙居民的中心枢纽&#xff0c;拥有超过100 万用户的蓬勃社区。它的主要使命是促进自我表达&#xff0c;释放创造力&#xff0c;构建梦想中的平行宇宙…

【HTML】自定义属性(data)

自定义属性 data: 的用法&#xff08;如何设置,如何获取) &#xff0c;有何优势&#xff1f; data-* 的值的获取和设置&#xff0c;2种方法: 传统方法 getAttribute() 获取 data- 属性值; setAttribute() 设置 data- 属性值getAttribute() 获取 data- 属性值; setAttribute()…

腾讯云SSL证书在阿里云添加域名DNS解析

场景说明 在阿里云申请并备案了域名&#xff0c;这个域名理论上可以在任何服务器上进行绑定。应用服务器部署到腾讯云&#xff0c;并在腾讯云申请了SSL证书&#xff0c;从而完成HTTPS转化。那么问题来了&#xff0c;腾讯提供的免费版SSL证书是一年有效期&#xff0c;到期后就需…

Cadence——布线部分相关教程

&#xff08;一&#xff09;PCB布线叠层与阻抗设计 PCB布线要区分有阻抗和无阻抗这个是根据频率和速率来区分&#xff0c;一般达100M以上就是有阻抗&#xff0c;单端信号一般是五十欧姆&#xff0c;差分信号一般一百欧姆 叠成是多层板由不同的介质压合而成&#xff0c;介质是&…