本文只贴出核心代码,如有其他需求,请私信Me.
一、pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.appleyk</groupId>
<artifactId>spring-boot-mongodb</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<description>spring-boot 集成mongodb,实现简单的对象存储和查询</description>
<!-- 继承官网最新父POM【假设当前项目不再继承其他POM】 -->
<!-- http://projects.spring.io/spring-boot/#quick-start -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
</parent>
<!-- 使用Java8,嘗試使用新特新【stream和lambda】 -->
<properties>
<java.version>1.8</java.version>
<geotools.version>18.0</geotools.version>
</properties>
<repositories>
<repository>
<id>osgeo</id>
<name>Open Source Geospatial Foundation Repository</name>
<url>http://download.osgeo.org/webdav/geotools/</url>
</repository>
</repositories>
<dependencies>
<!-- Starter POMs是可以包含到应用中的一个方便的依赖关系描述符集合 -->
<!-- 该Starters包含很多你搭建项目, 快速运行所需的依赖, 并提供一致的, 管理的传递依赖集。 -->
<!-- 大多数的web应用都使用spring-boot-starter-web模块进行快速搭建和运行。 -->
<!-- spring-boot-starter-web -->
<!-- 对全栈web开发的支持, 包括Tomcat和 spring-webmvc -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency><!-- 添加Mybatis、Spring-Mybatis依赖 -->
<!-- Spring 单元测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- JUnit单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<!-- 添加热部署 devtools:监听文件变动 -->
<!-- 当Java文件改动时,Spring-boo会快速重新启动 -->
<!-- 最简单的测试,就是随便找一个文件Ctrl+S一下,就可以看到效果 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<!-- optional=true,依赖不会传递 -->
<!-- 本项目依赖devtools;若依赖本项目的其他项目想要使用devtools,需要重新引入 -->
<optional>true</optional>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-mongodb -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<!-- 添加GeoTools依赖 -->
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-shapefile</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-swing</artifactId>
<version>${geotools.version}</version>
</dependency>
</dependencies>
</project>
二、工具类
WebMercatorUtils.java
package com.appleyk.utils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
/**
* 地图瓦片(切片) -- 墨卡托坐标切片工具
*
* @author yukun24@126.com
* @blob http://blog.csdn.net/appleyk
* @date 2018年4月4日-下午1:04:50
*/
public class WebMercatorUtils {
/**
* Web墨卡托 -- 互联网地图通用的地图投影方式,将椭圆形地图投影成平面上的正方形
* Bounds(地图范围)[minx,miny,maxx,maxy]
* 全世界墨卡托范围是:-20037580.3427892,-20037508.3427892,20037580.3427892,20037580.
* 3427892 从第四象限 --- 第一象限
*/
public static double minx = -20037508.3427892;
public static double maxx = 20037508.3427892;
/**
* 切片转范围
*
* @param x
* @param y
* @param level
* @return
*/
public static Envelope tileXYToNativeRectangle(int x, int y, int level) {
int xTiles = getNumberOfXTilesAtLevel(level);
int yTiles = getNumberOfYTilesAtLevel(level);
double xTileWidth = (maxx - minx) / xTiles;
double west = minx + x * xTileWidth;
double east = minx + (x + 1) * xTileWidth;
double yTileHeight = (maxx - minx) / yTiles;
double north = maxx - y * yTileHeight;
double south = maxx - (y + 1) * yTileHeight;
Envelope envelope = new Envelope(west, east, north, south);
return envelope;
}
/**
* 根据经纬度 + 切片级别 --- 计算 x:? y:?
*
* @param level
* @param longitude
* @param latitude
* @return
*/
public static Map<String, Integer> positionToTileXY(int level, double longitude, double latitude) {
Map<String, Integer> map = new HashMap<String, Integer>();
Coordinate coordinate = new Coordinate(longitude, latitude);
Coordinate c = lonLat2Mercator(coordinate);
// 计算在该级别下X方向切片数量
int xTiles = getNumberOfXTilesAtLevel(level);
// 计算在该级别下Y方向切片数量
int yTiles = getNumberOfYTilesAtLevel(level);
double overallWidth = maxx - minx;
double xTileWidth = overallWidth / xTiles;
double overallHeight = maxx - minx;
double yTileHeight = overallHeight / yTiles;
double distanceFromWest = c.x - minx;
double distanceFromNorth = maxx - c.y;
double xTileCoordinate = distanceFromWest / xTileWidth;
if (xTileCoordinate >= xTiles) {
xTileCoordinate = xTiles - 1;
}
map.put("X", (int) xTileCoordinate);
double yTileCoordinate = distanceFromNorth / yTileHeight;
if (yTileCoordinate >= yTiles) {
yTileCoordinate = yTiles - 1;
}
map.put("Y", (int) yTileCoordinate);
return map;
}
/**
* level级别下,X(瓦片的横向索引,起始位置为最左边,数值为0)方向的切片数量 -- 2的level次幂
*
* @param level
* @return
*/
private static Integer getNumberOfXTilesAtLevel(int level) {
return 1 << level;
}
/**
* level级别下,Y(瓦片的纵向索引,起始位置为最上面,数值为0)方向的切片数量 -- 2的level次幂
*
* @param level
* @return
*/
private static Integer getNumberOfYTilesAtLevel(int level) {
return 1 << level;
}
// ---- 瓦片总数量 就是 4的level次幂
/**
* 经纬度转墨卡托
*
* @param lonLat
* @return
*/
public static Coordinate lonLat2Mercator(Coordinate lonLat) {
double x = lonLat.x * 20037508.34 / 180;
double y = Math.log(Math.tan((90 + lonLat.y) * Math.PI / 360)) / (Math.PI / 180);
y = y * maxx / 180;
return new Coordinate(x, y);
}
/**
* 墨卡托转经纬度
*
* @param mercator
* @return
*/
public static Coordinate Mercator2lonLat(Coordinate mercator) {
double x = mercator.x / 20037508.34 * 180;
double y = mercator.y / 20037508.34 * 180;
y = 180 / Math.PI * (2 * Math.atan(Math.exp(y * Math.PI / 180)) - Math.PI / 2);
return new Coordinate(x, y);
}
/**
* 获得某个范围下某个级别下的切片XY的范围 ,比如X:[10,20], Y:[20,30]
*
* @param level
* -- 缩放级别
* @param minX
* -- 最小X == 西经(west)
* @param maxX
* -- 最大X == 东经(east)
* @param minY
* -- 最小Y == 北纬(north)
* @param maxY
* -- 最大Y == 南维(south)
* @return
*/
public static Map<String, List<Integer>> GetTileXYRange(int level, double minX, double maxX, double minY,
double maxY) {
Map<String, Integer> minXY = positionToTileXY(level, minX, minY);
Map<String, Integer> maxXY = positionToTileXY(level, maxX, maxY);
Integer minTileX = minXY.get("X");
Integer maxTileX = maxXY.get("X");
Integer minTileY = minXY.get("Y");
Integer maxTileY = maxXY.get("Y");
List<Integer> tileXrange = new ArrayList<>();
tileXrange.add(minTileX);
tileXrange.add(maxTileX);
List<Integer> tileYrange = new ArrayList<>();
tileYrange.add(minTileY);
tileYrange.add(maxTileY);
Map<String, List<Integer>> mapResult = new HashMap<>();
mapResult.put("X", tileXrange);
mapResult.put("Y", tileYrange);
return mapResult;
}
public static void main(String[] args) {
/**
* 郑州范围
*/
double minX = 112.729502;
double maxX = 114.210982;
double minY = 34.914062;
double maxY = 34.351984;
Map<String, Integer> minXY = positionToTileXY(15, minX, minY);
Map<String, Integer> maxXY = positionToTileXY(15, maxX, maxY);
Integer minTileX = minXY.get("X");
Integer maxTileX = maxXY.get("X");
Integer minTileY = minXY.get("Y");
Integer maxTileY = maxXY.get("Y");
System.err.println("x切片范围:[" + minTileX + "," + maxTileX + "]");
System.err.println("Y切片范围:[" + minTileY + "," + maxTileY + "]");
String url = "http://www.google.cn/maps/vt?lyrs=s@781&gl=cn&";
// x=46057&y=32219&z=16
int n = 0;
for (int i = minTileX; i <= maxTileX; i++) {
for (int j = minTileY; j <= maxTileY; j++) {
// System.err.println(url+"x="+i+"&y="+j+"&z=15");
n++;
}
}
System.err.println("郑州缩放级别15下的切片总量:" + n);
}
}
三、测试结果
四、谷歌切片服务请求
http://www.google.cn/maps/vt?lyrs=s@781&gl=cn&x=26779&y=13049&z=15
五、切片资源下载+本地磁盘存储
(1)
(2)
六、切片资源下载+MongoDB存储+查询
(1)
(2)
(3)