public class GlobalGeodetic {
private int tileSize;
private double resFact;
public GlobalGeodetic(String tmscompatible, int tileSize) {
this.tileSize = tileSize;
if (tmscompatible != null && tmscompatible.length() > 0) {
// Defaults the resolution factor to 0.703125 (2 tiles @ level 0)
this.resFact = 180.0D / (double)this.tileSize;
} else {
//Defaults the resolution factor to 1.40625 (1 tile @ level 0)
this.resFact = 360.0D / (double)this.tileSize;
}
}
public double[] lonlatToPixels(double lon, double lat, int zoom) {
double res = this.resFact / Math.pow(2.0D, (double)zoom);
return new double[]{(180.0D + lon) / res, (90.0D + lat) / res};
}
public int[] pixelsToTile(double px, double py) {
int tx = (int)(Math.ceil(px / (double)this.tileSize) - 1.0D);
int ty = (int)(Math.ceil(py / (double)this.tileSize) - 1.0D);
return new int[]{tx, ty};
}
public int[] lonlatToTile(double lon, double lat, int zoom) {
double[] pxpy = this.lonlatToPixels(lon, lat, zoom);
return this.pixelsToTile(pxpy[0], pxpy[1]);
}
public double resolution(int zoom) {
return this.resFact / Math.pow(2.0D, (double)zoom);
}
public int zoomForPixelSize(double pixelSize) {
for(int i = 0; i this.resolution(i)) {
if (i != 0) {
return i - 1;
}
return 0;
}
}
return 0;
}
public double[] tileBounds(int tx, int ty, int zoom) {
double res = this.resFact / Math.pow(2.0D, (double)zoom);
return new double[]{(double)(tx * this.tileSize) * res - 180.0D, (double)(ty * this.tileSize) * res - 90.0D, (double)((tx + 1) * this.tileSize) * res - 180.0D, (double)((ty + 1) * this.tileSize) * res - 90.0D};
}
public double[] tileLatLonBounds(int tx, int ty, int zoom) {
double[] b = this.tileBounds(tx, ty, zoom);
return new double[]{b[1], b[0], b[3], b[2]};
}
}
2、投影坐标系下计算类
目前绝大多数切片系统内部实现都是采用这种方式实现。
public class GlobalMercator {
private int tileSize;
private double initialResolution;
private double originSh;
//6378137 为地球半径
public GlobalMercator(int tileSize) {
this.tileSize = tileSize;
this.initialResolution = 2 * Math.PI * 6378137 / this.tileSize;
this.originShift = 2 * Math.PI * 6378137 / 2.0;
}
public double[] latLonToMeters(double lat, double lon) {
double mx = lon * this.originShift / 180.0;
double my = Math.log(Math.tan((90 + lat) * Math.PI / 360.0)) / (Math.PI / 180.0);
my = my * this.originShift / 180.0;
return new double[]{mx, my};
}
public double[] metersToLatLon(double mx, double my) {
double lon = (mx / this.originShift) * 180.0;
double lat = (my / this.originShift) * 180.0;
lat = 180 / Math.PI * (2 * Math.atan(Math.exp(lat * Math.PI / 180.0)) - Math.PI / 2.0);
return new double[]{lat, lon};
}
public double[] pixelsToMeters(int px, int py, int zoom) {
double res = this.resolution(zoom);
double mx = px * res - this.originShift;
double my = py * res - this.originShift;
return new double[]{mx, my};
}
public double[] metersToPixels(double mx, double my, int zoom) {
double res = this.resolution(zoom);
double px = (mx + this.originShift) / res;
double py = (my + this.originShift) / res;
return new double[]{px, py};
}
public int[] pixelsToTile(double px, double py) {
int tx = (int) (Math.ceil(px / (float) (this.tileSize)) - 1);
int ty = (int) (Math.ceil(py / (float) (this.tileSize)) - 1);
return new int[]{tx, ty};
}
public double[] pixelsToRaster(double px, double py, int zoom) {
double mapSize = this.tileSize << zoom;
return new double[]{px, mapSize - py};
}
public int[] metersToTile(double mx, double my, int zoom) {
double[] coordinate = this.metersToPixels(mx, my, zoom);
return this.pixelsToTile(coordinate[0], coordinate[1]);
}
public double[] tileBounds(int tx, int ty, int zoom) {
double[] minxy = pixelsToMeters(tx * this.tileSize, ty * this.tileSize, zoom);
double[] maxxy = pixelsToMeters((tx + 1) * this.tileSize, (ty + 1) * this.tileSize, zoom);
return new double[]{minxy[0], minxy[1], maxxy[0], maxxy[1]};
}
public double[] tileLatLonBounds(int tx, int ty, int zoom) {
double[] bounds = this.tileBounds(tx, ty, zoom);
double[] minLatLon = this.metersToLatLon(bounds[0], bounds[1]);
double[] maxLatlon = this.metersToLatLon(bounds[2], bounds[3]);
return new double[]{minLatLon[0], minLatLon[1], maxLatlon[0], maxLatlon[1]};
}
public double resolution(int zoom) {
return this.initialResolution / (Math.pow(2, zoom));
}
public int zoomForPixelSize(double pixelSize) {
for (int i = 0; i this.resolution(i)) {
if (i != 0) {
return i - 1;
} else return 0;
}
}
return 0;
}
//XYZ切片系统转googleTSM切片系统,其实就是坐标系的Y轴的变换,由原点左上角变换为原点左下角。
public int[] googleTile(int tx, int ty, int zoom) {
return new int[]{tx, ((int) (Math.pow(2, zoom)) - 1) - ty};
}
public String quadTree(int tx, int ty, int zoom) {
String quadKey = "";
ty = (int) (Math.pow(2, zoom) - 1 - ty);
for (int i = zoom; i > 0; i--) {
int digit = 0;
int mask = 1 < 0; i--) {
char digit = '0';
int mask = 1 < 0; i--) {
int mask = 1 << (i - 1);
switch (quadKey.charAt(levelOfDetail - i)) {
case '0':
break;
case '1':
tileX |= mask;
break;
case '2':
tileY |= mask;
break;
case '3':
tileX |= mask;
tileY |= mask;
break;
default:
throw new Exception("Invalid QuadKey digit sequence.");
}
}
return new int[]{tileX, tileY, levelOfDetail};
}
}
3、使用例子
根据XYZ计算经纬度范围
//地理坐标(EPSG:4326)下计算方式
double[] bbox = new GlobalGeodetic("", 256).tileLatLonBounds(x, y, z);
//投影坐标(EPSG:3857)下的计算方式
double[] bboxs = new GlobalMercator(256).tileLatLonBounds(x, y, z);
根据经纬度范围计算XYZ
//地理坐标(EPSG:4326)下计算方式
……待补充
//投影坐标(EPSG:3857)下的计算方式
Envelope envelope = new Envelope(xmin, xmax, ymin, ymax);
GlobalMercator mercator = new GlobalMercator(256);
double[] min = mercator.latLonToMeters(envelope.getMinY(), envelope.getMinX());
double[] max = mercator.latLonToMeters(envelope.getMaxY(), envelope.getMaxX());
//#region 计算
for (int tz = tmaxz; tz > tminz - 1; tz--) {
int[] tminxy = mercator.metersToTile(min[0], min[1], tz);
int[] tmaxxy = mercator.metersToTile(max[0], max[1], tz);
tminxy = new int[]{Math.max(0, tminxy[0]), Math.max(0, tminxy[1])};
tmaxxy = new int[]{(int) Math.min(Math.pow(2, tz) - 1, tmaxxy[0]), (int) Math.min(Math.pow(2, tz) - 1, tmaxxy[1])};
for (int tx = tminxy[0]; tx tminxy[1] - 1; ty--) {
//z,x,y坐标
}
}
}
//endregion
作者:JerFer