Java实现Google的S2算法工具类
WGS84坐标系 GCJ02坐标系 BD09坐标系的各种转换
WGS84坐标系 GCJ02坐标系 BD09坐标系的各种转换 Google S2 经纬度 转 CellId 经纬度 转 cellToken CellId 转 经纬度 判断当前cellId的level * 获取任意形状内所有S2块 * 可以用于区域内目标检索,根据cellid建立索引,查询区域内cellid in (list)的区域 S2计算点距离 计算地球上某个点是否在矩形区域内 计算点s2是否在圆中心为s1半径为capHeight的圆形区域内 判断点是否在任意形状内 计算两个区域是否有交集 求不同等级S2块包含的S2子块
S2 Demo
Java实现Google的S2算法工具类GoogleS2GoogleS2GoogleS2-Java文档类资源-CSDN下载
level (等级) | min area(最小面积) | max area(最大面积) | average area(平均面积) | units(单位) | Random cell 1 (UK)() min edge length(随机单元1 (UK)最小边长度) | Random cell 1 (UK) max edge length(随机单元格1 (UK)最大边长度) | Random cell 2 (US) min edge length(随机单元2 (US)最小边长度) | Random cell 2 (US) max edge length(随机单元格2(美国)最大边长度) | Number of cells(单元格数) |
0 | 85011012.19 | 85011012.19 | 85011012.19 | km2 | 7842 km | 7842 km | 7842 km | 7842 km | 6 |
1 | 21252753.05 | 21252753.05 | 21252753.05 | km2 | 3921 km | 5004 km | 3921 km | 5004 km | 24 |
2 | 4919708.23 | 6026521.16 | 5313188.26 | km2 | 1825 km | 2489 km | 1825 km | 2489 km | 96 |
3 | 1055377.48 | 1646455.5 | 1328297.07 | km2 | 840 km | 1167 km | 1130 km | 1310 km | 384 |
4 | 231564.06 | 413918.15 | 332074.27 | km2 | 432 km | 609 km | 579 km | 636 km | 1536 |
5 | 53798.67 | 104297.91 | 83018.57 | km2 | 210 km | 298 km | 287 km | 315 km | 6K |
6 | 12948.81 | 26113.3 | 20754.64 | km2 | 108 km | 151 km | 143 km | 156 km | 24K |
7 | 3175.44 | 6529.09 | 5188.66 | km2 | 54 km | 76 km | 72 km | 78 km | 98K |
8 | 786.2 | 1632.45 | 1297.17 | km2 | 27 km | 38 km | 36 km | 39 km | 393K |
9 | 195.59 | 408.12 | 324.29 | km2 | 14 km | 19 km | 18 km | 20 km | 1573K |
10 | 48.78 | 102.03 | 81.07 | km2 | 7 km | 9 km | 9 km | 10 km | 6M |
11 | 12.18 | 25.51 | 20.27 | km2 | 3 km | 5 km | 4 km | 5 km | 25M |
12 | 3.04 | 6.38 | 5.07 | km2 | 1699 m | 2 km | 2 km | 2 km | 100M |
13 | 0.76 | 1.59 | 1.27 | km2 | 850 m | 1185 m | 1123 m | 1225 m | 402M |
14 | 0.19 | 0.4 | 0.32 | km2 | 425 m | 593 m | 562 m | 613 m | 1610M |
15 | 47520.3 | 99638.93 | 79172.67 | m2 | 212 m | 296 m | 281 m | 306 m | 6B |
16 | 11880.08 | 24909.73 | 19793.17 | m2 | 106 m | 148 m | 140 m | 153 m | 25B |
17 | 2970.02 | 6227.43 | 4948.29 | m2 | 53 m | 74 m | 70 m | 77 m | 103B |
18 | 742.5 | 1556.86 | 1237.07 | m2 | 27 m | 37 m | 35 m | 38 m | 412B |
19 | 185.63 | 389.21 | 309.27 | m2 | 13 m | 19 m | 18 m | 19 m | 1649B |
20 | 46.41 | 97.3 | 77.32 | m2 | 7 m | 9 m | 9 m | 10 m | 7T |
21 | 11.6 | 24.33 | 19.33 | m2 | 3 m | 5 m | 4 m | 5 m | 26T |
22 | 2.9 | 6.08 | 4.83 | m2 | 166 cm | 2 m | 2 m | 2 m | 105T |
23 | 0.73 | 1.52 | 1.21 | m2 | 83 cm | 116 cm | 110 cm | 120 cm | 422T |
24 | 0.18 | 0.38 | 0.3 | m2 | 41 cm | 58 cm | 55 cm | 60 cm | 1689T |
25 | 453.19 | 950.23 | 755.05 | cm2 | 21 cm | 29 cm | 27 cm | 30 cm | 7.00E+15 |
26 | 113.3 | 237.56 | 188.76 | cm2 | 10 cm | 14 cm | 14 cm | 15 cm | 2.70E+16 |
27 | 28.32 | 59.39 | 47.19 | cm2 | 5 cm | 7 cm | 7 cm | 7 cm | 1.08E+17 |
28 | 7.08 | 14.85 | 11.8 | cm2 | 2 cm | 4 cm | 3 cm | 4 cm | 4.32E+17 |
29 | 1.77 | 3.71 | 2.95 | cm2 | 12 mm | 18 mm | 17 mm | 18 mm | 1.73E+18 |
30 | 0.44 | 0.93 | 0.74 | cm2 | 6 mm | 9 mm | 8 mm | 9 mm | 7.00E+18 |
package com.zz.meridian.utils.googleS2;/*** @author tiger* GPS位置信息接下来说下坐标系。目前主要有三种地理坐标系,如下:* 1、WGS84坐标系:即地球坐标系(World Geodetic System),国际上通用的坐标系。* 设备包含的GPS芯片或者北斗芯片获取的经纬度一般都是为WGS84地理坐标系,目前谷歌地图采用的是WGS84坐标系(中国范围除外)。* 2、GCJ02坐标系:即火星坐标系,国测局坐标系。是由中国国家测绘局制定。由WGS84坐标系经加密后的坐标系。谷歌中国和搜搜中国采用的GCJ02地理坐标系。* 3、BD09坐标系:百度坐标系,GCJ02坐标系经加密后的坐标系。* 4、其他(搜狗坐标系,图吧坐标系等)。大概率也是再GCJ02坐标系基础上加密生成的*/
public class PointS2Transform {public static double x_PI = 3.14159265358979324 * 3000.0 / 180.0;public static double PI = 3.1415926535897932384626;public static double a = 6378245.0;public static double ee = 0.00669342162296594323;/*** 百度坐标系 (BD-09) 与 火星坐标系 (GCJ-02)的转换* 即 百度 转 谷歌、高德** @param bd_lon* @param bd_lat* @returns {*[]}*/public static PointS2 bd09togcj02(double bd_lon, double bd_lat) {double x = bd_lon - 0.0065;double y = bd_lat - 0.006;double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_PI);double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_PI);double gg_lng = z * Math.cos(theta);double gg_lat = z * Math.sin(theta);PointS2 point = new PointS2(gg_lat, gg_lng);return point;}/*** 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换* 即谷歌、高德 转 百度** @param lng* @param lat* @returns {*[]}*/public static PointS2 gcj02tobd09(double lng, double lat) {double z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * x_PI);double theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * x_PI);double bd_lng = z * Math.cos(theta) + 0.0065;double bd_lat = z * Math.sin(theta) + 0.006;PointS2 point = new PointS2(bd_lat, bd_lng);return point;};/*** WGS84转GCj02** @param lng* @param lat* @returns {*[]}*/public static PointS2 wgs84togcj02(double lng, double lat) {double dlat = transformlat(lng - 105.0, lat - 35.0);double dlng = transformlng(lng - 105.0, lat - 35.0);double radlat = lat / 180.0 * PI;double magic = Math.sin(radlat);magic = 1 - ee * magic * magic;double sqrtmagic = Math.sqrt(magic);dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI);dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI);double mglat = lat + dlat;double mglng = lng + dlng;PointS2 point = new PointS2(mglat, mglng);return point;}/*** 84 to ⽕星坐标系 (GCJ-02) 是否离开了中国版** @param lat* @param lon*/public static PointS2 gps84_To_Gcj02(double lat, double lon) {if (outOfChina(lat, lon)) {return null;}double dLat = transformLat(lon - 105.0, lat - 35.0);double dLon = transformLon(lon - 105.0, lat - 35.0);double radLat = lat / 180.0 * PI;double magic = Math.sin(radLat);magic = 1 - ee * magic * magic;double sqrtMagic = Math.sqrt(magic);dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * PI);dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * PI);double mgLat = lat + dLat;double mgLon = lon + dLon;return new PointS2(mgLat, mgLon);}/*** ⽕星坐标系 GCJ02 转换为 WGS84** @param lng* @param lat* @returns {*[]}*/public static PointS2 gcj02towgs84(double lng, double lat) {double dlat = transformlat(lng - 105.0, lat - 35.0);double dlng = transformlng(lng - 105.0, lat - 35.0);double radlat = lat / 180.0 * PI;double magic = Math.sin(radlat);magic = 1 - ee * magic * magic;double sqrtmagic = Math.sqrt(magic);dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI);dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI);double mglat = lat + dlat;double mglng = lng + dlng;PointS2 point = new PointS2(mglat, mglng);return point;}/*** ⽕星坐标系 GCJ02 转换为 WGS84 是否离开了中国版* @param lon * @param lat * @return*/public static PointS2 gcj_To_Gps84(double lat, double lon) {PointS2 gps = transform(lat, lon);double lontitude = lon * 2 - gps.getLng();double latitude = lat * 2 - gps.getLat();return new PointS2(latitude, lontitude);}/*** 将 GCJ-02 坐标转换成 BD-09 坐标* @param gg_lat* @param gg_lon*/public static PointS2 gcj02_To_Bd09(double gg_lat, double gg_lon) {double x = gg_lon, y = gg_lat;double z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * PI);double theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * PI);double bd_lon = z * Math.cos(theta) + 0.0065;double bd_lat = z * Math.sin(theta) + 0.006;return new PointS2(bd_lat, bd_lon);}/*** 将 BD-09 坐标转换成GCJ-02 坐标* bd_lat * @param bd_lon * @return*/public static PointS2 bd09_To_Gcj02(double bd_lat, double bd_lon) {double x = bd_lon - 0.0065;double y = bd_lat - 0.006;double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * PI);double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * PI);double gg_lon = z * Math.cos(theta);double gg_lat = z * Math.sin(theta);return new PointS2(gg_lat, gg_lon);}/*** (BD-09)-->84** @param bd_lat* @param bd_lon* @return*/public static PointS2 bd09_To_Gps84(double bd_lat, double bd_lon) {PointS2 gcj02 = bd09_To_Gcj02(bd_lat, bd_lon);PointS2 map84 = gcj_To_Gps84(gcj02.getLat(),gcj02.getLng());return map84;}/*** is or not outOfChina* 是否离开了中国* @param lat* @param lon* @return*/public static boolean outOfChina(double lat, double lon) {if (lon < 72.004 || lon > 137.8347)return true;if (lat < 0.8293 || lat > 55.8271)return true;return false;}private static double transformlat(double lng, double lat) {double ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng));ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0;ret += (20.0 * Math.sin(lat * PI) + 40.0 * Math.sin(lat / 3.0 * PI)) * 2.0 / 3.0;ret += (160.0 * Math.sin(lat / 12.0 * PI) + 320 * Math.sin(lat * PI / 30.0)) * 2.0 / 3.0;return ret;}private static double transformlng(double lng, double lat) {double ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng));ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0;ret += (20.0 * Math.sin(lng * PI) + 40.0 * Math.sin(lng / 3.0 * PI)) * 2.0 / 3.0;ret += (150.0 * Math.sin(lng / 12.0 * PI) + 300.0 * Math.sin(lng / 30.0 * PI)) * 2.0 / 3.0;return ret;}public static PointS2 transform(double lat, double lon) {if (outOfChina(lat, lon)) {return new PointS2(lat, lon);}double dLat = transformLat(lon - 105.0, lat - 35.0);double dLon = transformLon(lon - 105.0, lat - 35.0);double radLat = lat / 180.0 * PI;double magic = Math.sin(radLat);magic = 1 - ee * magic * magic;double sqrtMagic = Math.sqrt(magic);dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * PI);dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * PI);double mgLat = lat + dLat;double mgLon = lon + dLon;return new PointS2(mgLat, mgLon);}public static double transformLat(double x, double y) {double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y+ 0.2 * Math.sqrt(Math.abs(x));ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0;ret += (20.0 * Math.sin(y * PI) + 40.0 * Math.sin(y / 3.0 * PI)) * 2.0 / 3.0;ret += (160.0 * Math.sin(y / 12.0 * PI) + 320 * Math.sin(y * PI / 30.0)) * 2.0 / 3.0;return ret;}public static double transformLon(double x, double y) {double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1* Math.sqrt(Math.abs(x));ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0;ret += (20.0 * Math.sin(x * PI) + 40.0 * Math.sin(x / 3.0 * PI)) * 2.0 / 3.0;ret += (150.0 * Math.sin(x / 12.0 * PI) + 300.0 * Math.sin(x / 30.0* PI)) * 2.0 / 3.0;return ret;}
}
package com.zz.meridian.utils.googleS2;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@NoArgsConstructor
@AllArgsConstructor
public class PointS2 {double lat;double lng;
}
package com.zz.meridian.utils.googleS2;import com.google.common.collect.Lists;
import com.google.common.geometry.*;import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;/*** @author tiger* 必须使用-S2使用的是WGS84坐标* 如果你获得的是WGS84坐标-百度或者高德的地理坐标,请将其转换为GPS-WGS84坐标* 由于google s2默认使用gps坐标系,在国内无法使用,需要转换为国内的gcj坐标或者bd09坐标* 主要包含3类方法:* getS2RegionByXXX* 获取给定经纬度坐标对应的S2Region,该region可用于获取cellId,或用于判断包含关系* getCellIdList* 获取给定region的cellId,并通过childrenCellId方法控制其严格遵守minLevel* contains* 对于指定S2Region,判断经纬度或CellToken是否在其范围内*/
/*
包<dependency><groupId>io.sgr</groupId><artifactId>s2-geometry-library-java</artifactId><version>1.0.0</version></dependency>*/
public class GoogleS2 {/*** 经纬度 转 S2CellId** @param lat 维度* @param lng 经度* @param currentLevel level选择级别*/public static S2CellId latLonToS2LatLng(double lat, double lng, int currentLevel) {S2LatLng s2LatLng = S2LatLng.fromDegrees(lat, lng);S2CellId cellId = S2CellId.fromLatLng(s2LatLng).parent(currentLevel);return cellId;}/*** 经纬度 转 CellId** @param lat 维度* @param lng 经度* @param currentLevel level选择级别*/public static Long latLonToCellId(double lat, double lng, int currentLevel) {S2LatLng s2LatLng = S2LatLng.fromDegrees(lat, lng);S2CellId cellId = S2CellId.fromLatLng(s2LatLng).parent(currentLevel);return cellId.id();}/*** 经纬度 转 cellToken** @param lat 维度* @param lng 经度* @param currentLevel level选择级别*/public static String latLonToCellToken(double lat, double lng, int currentLevel) {try {S2LatLng s2LatLng = S2LatLng.fromDegrees(lat, lng);S2CellId cellId = S2CellId.fromLatLng(s2LatLng).parent(currentLevel);return cellId.toToken();} catch (Exception e) {e.printStackTrace();return null;}}/*** CellId 转 经纬度** @param cellId 是 S2CellId.id();* @return*/public static PointS2 cellIdToLatLon(Long cellId) {S2LatLng s2LatLng = new S2CellId(cellId).toLatLng();double lat = s2LatLng.latDegrees();double lng = s2LatLng.lngDegrees();return new PointS2(lat, lng);}/*** cellToken 转 经纬度** @param cellToken* @return*/public static PointS2 cellTokenToLatLon(String cellToken) {S2LatLng latLng = new S2LatLng(S2CellId.fromToken(cellToken).toPoint());return new PointS2(latLng.latDegrees(), latLng.lngDegrees());}/*** 判断region是否包含指定经纬度坐标** @param region* @param lat* @param lon* @return*/public static boolean contains(S2Region region, double lat, double lon) {S2LatLng s2LatLng = S2LatLng.fromDegrees(lat, lon);try {boolean contains = region.contains(new S2Cell(s2LatLng));return contains;} catch (NullPointerException e) {e.printStackTrace();return false;}}/*** 判断当前cellId的level** @param cellId* @return*/public static int getLevel(long cellId) {int n = 0;while (cellId % 2 == 0) {cellId = cellId / 2;n++;}return 30 - n / 2;}/*** 获取任意形状内所有S2块* 可以用于区域内目标检索,根据cellid建立索引,查询区域内cellid in (list)的区域** @param vertices 形成多边形的点集合* @return*/private static List<Long> vertices(List<PointS2> vertices) {//因为x一般表示经度 y轴表示纬度所以这儿需要参数需要对应一下List<S2Point> collect = vertices.stream().map(e -> S2LatLng.fromDegrees(e.getLng(), e.getLat()).toPoint()).collect(Collectors.toList());S2Loop s2Loop = new S2Loop(collect);S2Polygon cap = new S2Polygon(s2Loop); //创建多边形//S2Region cap 任意区域S2RegionCoverer coverer = new S2RegionCoverer();//最小格子和最大格子,总格子数量coverer.setMinLevel(7);//设置最小级别coverer.setMaxLevel(15);//设置最大级别coverer.setMaxCells(500);//设置最大CellList<S2CellId> list = coverer.getCovering(cap).cellIds();
// for (S2CellId s : list) {
// System.out.println(s.id());
// }return list.stream().map(S2CellId::id).collect(Collectors.toList());}/*** S2计算距离** @param s1 点1的经度 纬度* @param s2 点2的经度 纬度* @return*/public static double distance(PointS2 s1, PointS2 s2) {S2LatLng startS2 = S2LatLng.fromDegrees(s1.getLat(), s1.getLng());S2LatLng endS2 = S2LatLng.fromDegrees(s2.getLat(), s2.getLng());double distance = startS2.getEarthDistance(endS2);return distance;}/*** 计算地球上某个点是否在矩形区域内* 矩形的左下角点和矩形的右上角点通过纬度来判断,高低不然算不出来* @param s1 生成矩形的经纬度s1* @param s2 生成矩形的经纬度s2* @param s3 判断s3点是否在上面s1和s2的矩形中* @return*/public static boolean pointRectangleArea(PointS2 s1, PointS2 s2, int desLevel, PointS2 s3) {S2LatLngRect rect = null;if (s1.getLat() > s2.getLat()) {//两个点可以经纬度-构建S2矩形rect = new S2LatLngRect(S2LatLng.fromDegrees(s2.getLat(), s2.getLng()),S2LatLng.fromDegrees(s1.getLat(), s1.getLng()));} else {//两个点可以经纬度-构建S2矩形rect = new S2LatLngRect(S2LatLng.fromDegrees(s1.getLat(), s1.getLng()),S2LatLng.fromDegrees(s2.getLat(), s2.getLng()));}//设置矩形的大小S2RegionCoverer coverer = new S2RegionCoverer();//设置cellcoverer.setMinLevel(7);coverer.setMaxLevel(15);coverer.setMaxCells(500);S2CellUnion covering = coverer.getCovering(rect);S2LatLng s2LatLng = S2LatLng.fromDegrees(s3.getLat(), s3.getLng());return covering.contains(s2LatLng.toPoint());}/*** 计算点s2是否在圆中心为s1半径为capHeight的圆形区域内** @param s1* @param capHeight* @param s2* @return*/public static boolean pointGardenArea(PointS2 s1, double capHeight, PointS2 s2) {S2LatLng s2LatLng = S2LatLng.fromDegrees(s1.getLat(), s1.getLng());S2Cap cap = S2Cap.fromAxisHeight(s2LatLng.toPoint(), capHeight);S2LatLng s2LatLng2 = S2LatLng.fromDegrees(s2.getLat(), s2.getLng());boolean contains = cap.contains(s2LatLng2.toPoint());return contains;}/*** 判断点是否在任意形状内** @param vertices 形成多边形的点集合* @param s 判断的点* @return*/public static boolean pointPolygonArea(List<PointS2> vertices, PointS2 s) {//因为x一般表示经度 y轴表示纬度所以这儿需要参数需要对应一下List<S2Point> collect = vertices.stream().map(e -> S2LatLng.fromDegrees(e.getLng(), e.getLat()).toPoint()).collect(Collectors.toList());S2Loop s2Loop = new S2Loop(collect);S2Polygon polygon = new S2Polygon(s2Loop); //创建多边形S2Point s2Point = S2LatLng.fromDegrees(s.getLat(), s.getLng()).toPoint();boolean contains = polygon.contains(s2Point);return contains;}/*** 计算两个区域是否有交集** @param vertices 形成多边形的点集合1* @param vertices2 形成多边形的点集合2* @return*/public static boolean pointUniteArea(List<PointS2> vertices, List<PointS2> vertices2) {//因为x一般表示经度 y轴表示纬度所以这儿需要参数需要对应一下List<S2Point> collect = vertices.stream().map(e -> S2LatLng.fromDegrees(e.getLng(), e.getLat()).toPoint()).collect(Collectors.toList());//因为x一般表示经度 y轴表示纬度所以这儿需要参数需要对应一下List<S2Point> collect2 = vertices2.stream().map(e -> S2LatLng.fromDegrees(e.getLng(), e.getLat()).toPoint()).collect(Collectors.toList());S2Loop s2Loop = new S2Loop(collect);S2Polygon polygon = new S2Polygon(s2Loop);S2Loop s2Loop2 = new S2Loop(collect2);S2Polygon polygon2 = new S2Polygon(s2Loop2);S2RegionCoverer coverer = new S2RegionCoverer();//设置cellcoverer.setMinLevel(7);//设置最小级别coverer.setMaxLevel(15);//设置最大级别coverer.setMaxCells(500);//设置最大CellS2CellUnion covering = coverer.getCovering(polygon2);for (S2CellId s2CellId : covering.cellIds()) {boolean b = polygon.mayIntersect(new S2Cell(s2CellId));if (b) {System.out.println("两个区域之间含有交集.....");}return b;}return false;}/*** 不同等级S2块包含的S2子块** @param s 自己的点* @param level 自己的等级* @param desLevel 被计算的格子等级,注意:等级越大算的就越多* @return*/public static List<S2CellId> childrenCellId(PointS2 s, Integer level, Integer desLevel) {S2LatLng s2LatLng = S2LatLng.fromDegrees(s.getLat(), s.getLng());S2CellId cellId = S2CellId.fromLatLng(s2LatLng).parent(level);return childrenCellId(cellId, cellId.level(), desLevel);}//递归调用,每个格子一分为四private static List<S2CellId> childrenCellId(S2CellId s2CellId, Integer curLevel, Integer desLevel) {if (curLevel < desLevel) {//计算当前格子每个格子的差值long interval = (s2CellId.childEnd().id() - s2CellId.childBegin().id()) / 4;List<S2CellId> s2CellIds = Lists.newArrayList();for (int i = 0; i < 4; i++) {long id = s2CellId.childBegin().id() + interval * i;s2CellIds.addAll(childrenCellId(new S2CellId(id), curLevel + 1, desLevel));}return s2CellIds;} else {return Lists.newArrayList(s2CellId);}}/*** 任意形状内所有指定等级的S2块** @param vertices 多边形的点* @param desevel 需要计算的内部的s2块的等级* @return*/public static List<S2CellId> childrenCellId(List<PointS2> vertices, int desevel) {List<S2Point> collect = vertices.stream().map(e -> S2LatLng.fromDegrees(e.getLng(), e.getLat()).toPoint()).collect(Collectors.toList());S2Loop s2Loop = new S2Loop(collect);S2Polygon polygon = new S2Polygon(s2Loop);S2RegionCoverer coverer = new S2RegionCoverer();//设置cellcoverer.setMinLevel(6);//设置最小级别 108km~151kmcoverer.setMaxLevel(11);//设置最大级别 3km~5kmcoverer.setMaxCells(500);//设置最大CellS2CellUnion covering = coverer.getCovering(polygon);List<S2CellId> s2CellIds = covering.cellIds();int i=0;List<S2CellId> list=new ArrayList<>();for (S2CellId s2CellId : s2CellIds) {List<S2CellId> s2CellIds1 = childrenCellId(s2CellId, s2CellId.level(), desevel);list.addAll(s2CellIds1);}return list;}public static void main(String[] args) {double lat = 30.2;double lng = 116.3;int currentLevel = 13;S2LatLng s2LatLng = S2LatLng.fromDegrees(lat, lng);S2CellId cellId = S2CellId.fromLatLng(s2LatLng).parent(currentLevel);System.err.println(cellId);String s = cellId.toToken();System.err.println(s);/* System.err.println("------------------------------");System.err.println(latLonToCellToken(lat,lng,1));System.err.println(latLonToCellToken(lat,lng,2));System.err.println(latLonToCellToken(lat,lng,3));System.err.println(latLonToCellToken(lat,lng,4));System.err.println(latLonToCellToken(lat,lng,5));System.err.println(latLonToCellToken(lat,lng,6));System.err.println(latLonToCellToken(lat,lng,7));System.err.println(latLonToCellToken(lat,lng,8));System.err.println(latLonToCellToken(lat,lng,9));System.err.println(latLonToCellToken(lat,lng,10));System.err.println(latLonToCellToken(lat,lng,11));System.err.println(latLonToCellToken(lat,lng,12));System.err.println(latLonToCellToken(lat,lng,13));System.err.println(latLonToCellToken(lat,lng,14));System.err.println(latLonToCellToken(lat,lng,15));System.err.println(latLonToCellToken(lat,lng,16));System.err.println(latLonToCellToken(lat,lng,17));System.err.println(latLonToCellToken(lat,lng,18));System.err.println(latLonToCellToken(lat,lng,19));System.err.println(latLonToCellToken(lat,lng,20));System.err.println(latLonToCellToken(lat,lng,21));System.err.println(latLonToCellToken(lat,lng,30));System.err.println("------------------------------");*/PointS2 pointS2 = cellIdToLatLon(cellId.id());System.err.println(pointS2);double distance = distance(new PointS2(55.8241, 137.8347), new PointS2(55.8271, 137.8347));System.err.println("距离为:" + distance + " m");boolean b = pointRectangleArea(new PointS2(41.808006669390046, 111.495546258779), new PointS2(47.55467105799515, 117.6168335999181),30, new PointS2(45.47161041105891, 114.84087253252726));System.err.println("矩形-----------------" + b);boolean b2 = pointGardenArea(new PointS2(112.030500, 27.970271), 600.5, new PointS2(22.629164, 114.025514));System.err.println(b2);ArrayList<PointS2> pointS2s = new ArrayList<>();pointS2s.add(new PointS2(41.200195, 97.760681));pointS2s.add(new PointS2(41.827161, 103.119335));pointS2s.add(new PointS2(36.507585, 103.688463));pointS2s.add(new PointS2(35.895869, 98.743842));pointS2s.add(new PointS2(41.253179, 97.700277));boolean b1 = pointPolygonArea(pointS2s, new PointS2(39.470948, 100.302180));System.err.println("多边形-----------" + b1);System.err.println("多边形2-----------" + Ryamethod.ray2(39.470948f, 100.302180f, pointS2s));// boolean b3 = pointUniteArea(pointS2s, pointS2s);ArrayList<PointS2> pointS2s1 = Lists.newArrayList(new PointS2(1, 2), new PointS2(3, 4));boolean b3 = pointUniteArea(pointS2s, pointS2s1);System.err.println(b3);// List<S2CellId> s2CellIds1 = childrenCellId(new PointS2(39.470948, 100.302180), 10, 12);
// for (S2CellId s2CellId : s2CellIds1) {
// System.err.println("点下的s2的token------"+s2CellId.toToken());
// }List<S2CellId> s2CellIds = childrenCellId(pointS2s, 10);
// for (S2CellId s2CellId : s2CellIds) {
// System.err.println("token------"+s2CellId.toToken());
// }}}
Java实现Google的S2算法工具类相关推荐
- Google的S2算法原理以及使用Java版本--部分参考自《高效的多维空间点索引算法》
文章目录 相关资料 1.S2算法是什么? 2.为什么要使用S2算法? 3.S2的原理是什么? 1)球面坐标变换 2)球面坐标转平面坐标(降维) remark: 3)球面矩形投影修正 4)点与坐标轴点相 ...
- 国密算法java语言的实现:利用bcprov和hutool库分别实现国密SM4算法工具类,对称密钥
SM4算法成为行业标准: SM4分组密码算法是2012年3月21日实施的一项行业标准: 2021年6月25日,我国SM4分组密码算法作为国际标准ISO/IEC 18033-3:2010/AMD1:20 ...
- Java学习总结:58(Collections工具类)
Collections工具类 Java提供了一个集合的工具类--Collections,这个工具类可以实现List.Set.Map集合的操作.Collections类的常用方法如下: No. 方法 类 ...
- java配置文件工具类,java项目加载配置文件的工具类
java项目加载配置文件的工具类 package com.loadproperties; import java.io.IOException; import java.io.InputStream; ...
- Java 图片添加数字暗水印工具类
Java 图片添加数字暗水印工具类. package cnki.thesis.common.utils;import org.opencv.core.*;import java.util.ArrayL ...
- Java时间戳与日期格式转换工具类
Java时间戳与日期格式转换工具类 在做web开发时,有时候需要用到时间戳,在前台进行日期转换,从数据库中取出来是日期格式的,这里记录下使用的转换工具类(这些都是静态方法,通过类名.方法的形式即可调用 ...
- java获取客户端的IP地址工具类
java获取客户端的IP地址工具类 import java.net.InetAddress; import java.net.UnknownHostException;import javax.ser ...
- Java判断不为空的工具类总结
Java判断不为空的工具类总结 1.Java判断是否为空的工具类,可以直接使用.包含,String字符串,数组,集合等等. 1 package com.bie.util; 2 3 import jav ...
- (10)Java泛型-Map集合-集合框架工具类-可变参数-静态导入
-- 部分1.5新特性Java泛型-Map集合-集合框架工具类 泛型 概述: JDK1.5版本以后出现的新特性,用于解决安全问题,是一个类型安全机制. 对于泛型可以这样理解: 没有使用泛型时,只要是对 ...
最新文章
- java spring scope_如何在Spring中自定义scope的方法示例
- c++读取utf8文件_【Python】File文件对象
- 【云快讯】之四十五《Google在云服务领域认输?不,我们的重点是大数据》
- ASP.NETMVC View页面 序列化 c# Model 为 Json字符串
- 【Selenium】导出成py脚本的基础使用
- Qt 使用正则表达式进行字符串替换
- 如何基于K8s构建下一代DevOps平台?
- 用Java描述数据结构之线性表的顺序存储(顺序表),ArrayList及其方法的介绍
- 直接请求接口_「软件测试教程」基于postman进行接口测试实战
- 显式Intent 和隐式 Intent 的区别
- Vmware Update Manager安装错误,错误代码:25085
- sublime text3插件TrailingSpaces无法使用的解决方法
- 一文带你弄懂 CDN 技术的原理
- AD快捷键还原为默认配置
- 引用 孙悟空的师傅菩提祖师的真实真份和镇元大仙辈份排名+四大灵猴
- 计算机弹奏两只老虎爱跳舞,原神风物之诗琴乐谱大全 原神风物之诗琴谱乐谱弹奏攻略...
- c语言多个自我介绍编码,代码自我介绍.doc
- 精选大数据面试真题10道(附答案详细解析)
- 无线降噪耳机品牌推荐,值得入手的四款降噪蓝牙耳机
- 使用新浪微博登录组件
热门文章
- 二进制加权电容器阵列的构建公共质心布局和布线
- no package identifier when getting value for resource number 0x00000005
- android系统升级实现,疯狂升级的Android系统
- 三月模拟题——炉石传说
- 如何利用语音评测技术设计英语口语选择题
- 2020-10-23 集合+序列化+递归+多线程+泛型+枚举+单例+反射小记
- mysql学习应用_MySQL学习从这里出发!
- wps大纲栏显示在右边_5分钟帮你搞定PPT!金山偷偷上线WPS智能PPT完全免费
- vmvare虚拟机无法读取ntfs的U盘解决方法,以及更换镜像下载源
- JavaScript中事件的绑定与解绑