Spring-Boot -- 墨卡托坐标+切片转换工具


本文只贴出核心代码,如有其他需求,请私信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)






  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值