百度API提供了在地图上画圆形的类,但画出圆只能是实心圆,也就是不能实现让圆内没有颜色而圆外有颜色的镂空圆。那么我们应该实现镂空圆呢?
首先我发现API还提供一个画多边形的类。而一个镂空圆分解成两个多边形,即横着一刀切在这个镂空圆上,上下两部分各为一个多边形。于是我试着用这种方法画了一个镂空圆,代码详见:百度api中实现 内嵌圆
但实现上述镂空圆时我的思路还是讲经纬度看出笛卡尔坐标系上的x,y,然而经纬度在并不是均匀分布的,这也导致了用上述方法画出的圆一旦离开了赤道就会变成椭圆。
这个问题很棘手,首先我们分析问题,圆的失真是由于经纬度分布不均匀导致的,所以我们不能用笛卡尔坐标系来思考问题,而应该将经纬度转换成球坐标系。维度的范围是大于-90度而小于90度,而球坐标系中的φ的范围是0到180,同理,经度的范围是-180,180,而θ的范围是0,360。很容易找到他们的关系,即φ = 90-维度,
θ = 180-经度;转换公式:(维度,经度) = (φ,θ,R)其中R = 地球半径;
但是得到球坐标系坐标之后分析问题还是十分困难,我们应该将问题转换成我们熟悉的,即将球坐标系转换吃笛卡尔坐标系:
公式如下:
r = √(x²+y²+z²)
θ = arccos[z/√(x²+y²+z²)]
φ = arctan(y/x)
代码实现:
static V3 changeToXyz(LatLng lng){ V3 v = new V3(); if (lng.latitude<0){ v.var1 = 90 + Math.abs(lng.latitude); }else { v.var1 = 90 - lng.latitude ; } if(lng.longitude<0){ v.var2 = 180 + Math.abs(lng.longitude); }else { v.var2 = 180 - lng.longitude; } v.var3 = R; V3 temp = new V3(); temp.var1 = v.var3*Math.sin(Math.PI*v.var1/180)*Math.cos(Math.PI*v.var2/180); temp.var2 = v.var3*Math.sin(Math.PI*v.var1/180)*Math.sin(Math.PI*v.var2/180); temp.var3 = v.var3*Math.cos(Math.PI*v.var1/180); return temp; }V3类是我实现vector3的类(即3维向量)
代码如下:
static class V3 { double var1 = 0.0; double var2 = 0.0; double var3 = 0.0; V3(){ } V3(Double var1,Double var2, Double var3){ this.var1 = var1; this.var2 = var2; this.var3 = var3; } public static V3 add(V3 v1, V3 v2){ return new V3(v1.var1+v2.var1,v1.var2+v2.var2,v1.var3+v2.var3); } }得到笛卡尔坐标系之后我们终于可以分析问题了,在地图上画圆实际上就是在球体上画圆,我们可以令vector1为地图上的球心(0,0,0)到圆心center的向量,我们在 vector1上任意取一点half,过这点垂直于vector1的平面与球面相交的部分我们要求的圆形,而这个圆形正是以half为圆心的圆形,我们设这个圆形的半径是r。通过r我们能求出half的坐标。
Double rate = Math.sqrt(R*R-r*r)/R; V3 half = new V3(rate*center.var1,rate*center.var2,rate*center.var3);我们知道这个圆任意一条半径都是和vector1垂直的,即我们只要知道任意一条半径所在的向量的表达式遍可以通过旋转角 θ1求出所有半径所在的向量的表达式。为了方便我们选择一条在x,y平面上的向量作为这个起始向量vector2(这样的目的是为了让起始映射到经纬度坐标系之后与精度轴平行).
公式:
(x,y,z) = (1,-center.x/center.y,0)
然后我们让这条向量绕着center旋转,向量旋转公式:
代码如下
public static V3 getTemp(V3 center,Double ang){ center = getmR(center,1.0); Double x = center.var1; Double y = center.var2; Double z = center.var3; Matrix a = new Matrix(3,3); Matrix b = new Matrix(3,3); Matrix i = new Matrix(3,3); double[][] A = new double[3][3]; A[0][0] = x * x; A[0][1] = x * y; A[0][2] = x * z; A[1][0] = y * x; A[1][1] = y * y; A[1][2] = y * z; A[2][0] = z * x; A[2][1] = z * y; A[2][2] = z * z; double[][] B = new double[3][3]; B[0][0] = 0; B[0][1] = -z; B[0][2] = y; B[1][0] = z; B[1][1] = 0; B[1][2] = -x; B[2][0] = -y; B[2][1] = x; B[2][2] = 0; double[][] I = new double[3][3]; I[0][0] = 1; I[0][1] = 0; I[0][2] = 0; I[1][0] = 0; I[1][1] = 1; I[1][2] = 0; I[2][0] = 0; I[2][1] = 0; I[2][2] = 1; a.init(A); b.init(B); i.init(I); Matrix m = new Matrix(3,3); m = i.Minus(a); m = m.mMultiply(Math.cos(ang)); Matrix t = new Matrix(3,3); t = b; t = t.mMultiply(Math.sin(ang)); m = m.Plus(t); m = m.Plus(a); m = m.Trans(); t = new Matrix(3,1); double[][] T = new double[3][1]; T[0][0] = 1.0;T[1][0] =-1*center.var1/center.var2;T[2][0] =0.0; t.init(T); m = m.Multiply(t); m = m.Trans(); return new V3(m.mat[0][0],m.mat[0][1],m.mat[0][2]); }
介绍一下用到的Matrix 类
import java.util.Random; public class Matrix { public int row; public int rank; public double[][] mat; public Matrix(int a, int b) { row = a; rank = b; mat = new double[row][rank]; } public void New(){ Random rand=new Random(); for (int i = 0; i < row; i++) for (int j = 0; j < rank; j++) mat[i][j]=rand.nextInt(100); } public void init(double[][] in){ mat = in; } public void Output() { System.out.println("Matrix=:"); for (int i = 0; i < row; i++) { for (int j = 0; j < rank; j++) System.out.print(mat[i][j] + " "); System.out.println(); } System.out.println(); } public Matrix Plus(Matrix a) { Matrix c=new Matrix(row,rank); if (a.row == row && a.rank == rank) { for (int i = 0; i < row; i++) for (int j = 0; j < rank; j++) c.mat[i][j] = mat[i][j] + a.mat[i][j]; } else { System.out.println("matrixAdd error!"); } return c; } public Matrix Minus(Matrix a) { Matrix c=new Matrix(row,rank); if (a.row == row && a.rank == rank) { for (int i = 0; i <row; i++) for (int j = 0; j <rank; j++) c.mat[i][j] = mat[i][j] - a.mat[i][j]; } else { System.out.println("matrixMinus error!"); } return c; } public Matrix mMultiply(double a){ for(int i=0;i<row;i++){ for(int j=0;j<rank;j++){ mat[i][j] *= a; } } return this; } public Matrix Multiply(Matrix a) { Matrix c = new Matrix(row, a.rank); if (rank == a.row) { for (int i = 0; i < c.row; i++) for (int j = 0; j < c.rank; j++) { double sum = 0; for (int k = 0; k < rank; k++) sum += mat[i][k] * a.mat[k][j]; c.mat[i][j] = sum; } } else { System.out.println("matrix Multiply errors!"); } return c; } public Matrix Trans() { Matrix c = new Matrix(rank,row); for (int i = 0; i < c.row; i++) for (int j = 0; j < c.rank; j++) c.mat[i][j] = mat[j][i]; return c; } public Matrix Invs() { int rw = row, rk = rank; Matrix imat = new Matrix(rw, rk); Matrix jmat = new Matrix(rw, rk); for (int i = 0; i < rw; i++) for (int j = 0; j < rw; j++) jmat.mat[i][j] = mat[i][j]; for (int i = 0; i < rw; i++) for (int j = 0; j < rw; j++) imat.mat[i][j] = 0; for (int i = 0; i < rw; i++) imat.mat[i][i] = 1; for (int i = 0; i < rw; i++) { for (int j = 0; j < rw; j++) { if (i != j) { double t = jmat.mat[j][i] / jmat.mat[i][i]; for (int k = 0; k < rw; k++) { jmat.mat[j][k] -= jmat.mat[i][k] * t; imat.mat[j][k] -= imat.mat[i][k] * t; } } } } for (int i = 0; i < rw; i++) if (jmat.mat[i][i] != 1) { double t = jmat.mat[i][i]; for (int j = 0; j < rw; j++) { jmat.mat[i][j] = jmat.mat[i][j] / t; imat.mat[i][j] = imat.mat[i][j] / t; } } return imat; } }
好了,既然已经求到了每一个半径向量,那么我们用这个向量转换成膜为1的向量,然后加上half就能求出对应的点,把点连起来就是一个圆了。
以下是源码:
package com.example.administrator.pass.tools; import com.baidu.mapapi.map.OverlayOptions; import com.baidu.mapapi.map.PolygonOptions; import com.baidu.mapapi.map.Stroke; import com.baidu.mapapi.model.LatLng; import java.util.ArrayList; import java.util.List; /** * Created by Administrator on 11/10 0010. */ public class smoke { static Double R = 6371004.0; static V3 changeToXyz(LatLng lng){ V3 v = new V3(); if (lng.latitude<0){ v.var1 = 90 + Math.abs(lng.latitude); }else { v.var1 = 90 - lng.latitude ; } if(lng.longitude<0){ v.var2 = 180 + Math.abs(lng.longitude); }else { v.var2 = 180 - lng.longitude; } v.var3 = R; V3 temp = new V3(); temp.var1 = v.var3*Math.sin(Math.PI*v.var1/180)*Math.cos(Math.PI*v.var2/180); temp.var2 = v.var3*Math.sin(Math.PI*v.var1/180)*Math.sin(Math.PI*v.var2/180); temp.var3 = v.var3*Math.cos(Math.PI*v.var1/180); return temp; } static LatLng getLatLng(V3 in){ Double x = in.var1; Double y = in.var2; Double z = in.var3; Double r = Math.sqrt(x*x+y*y+z*z); Double ceta = Math.atan(y/x);//x Double gama = Math.acos(z/r); //z if (x==0.0&&y==0.0){ ceta = 0.0; } ceta = Math.abs(ceta); if(x>0&&y>=0){ ceta = ceta; } if(x<=0&&y>0){ ceta = Math.PI/2-ceta; ceta += Math.PI/2; } if(x<0&&y<=0){ ceta += Math.PI; } if(x>=0&&y<0){ ceta = Math.PI/2-ceta; ceta += Math.PI/2*3; } Double latitude = (gama/Math.PI)*180; Double longitude = (ceta/Math.PI)*180; if (latitude >= 90){ latitude = -1*(latitude-90); }else { latitude = 90 - latitude; } if (longitude >=180){ longitude = -1*(longitude-180); }else { longitude = 180 - longitude; } return new LatLng(latitude,longitude); } V3 test(V3 in){ Double x = in.var1; Double y = in.var2; Double z = in.var3; Double r = Math.sqrt(x*x+y*y+z*z); Double ceta = Math.PI/2 + Math.atan(y/x); Double gama = Math.acos(z/r); return (new V3(ceta,gama,r)); } public static OverlayOptions getOverlayOptions(LatLng Center, Double r, int acc){ List<LatLng> pts = new ArrayList<LatLng>(); getLatLng(changeToXyz(new LatLng(0.0,0.0))); V3 center = changeToXyz(Center); Double rate = Math.sqrt(R*R-r*r)/R; V3 half = new V3(rate*center.var1,rate*center.var2,rate*center.var3); V3 temp = new V3(); V3 mR = new V3(); LatLng lat = new LatLng(0.0,0.0); for(int i=0;i<acc/2+1;i++){ // temp = new V3(R*Math.cos((2*Math.PI)/acc*i),R*Math.sin((2*Math.PI)/acc*i),0.0); temp = getTemp(center,(2*Math.PI)/acc*i); // mR = getvertical(center,temp,r); mR = getmR(temp,r); // System.out.println(mR.var1+"%%%%%%%"+mR.var2+"%%%%%%%%"+mR.var3); mR = V3.add(mR,half); lat = getLatLng(mR); System.out.println(lat.latitude+"**********"+lat.longitude); pts.add(lat); } LatLng pt1 = new LatLng(lat.latitude,180); pts.add(pt1); LatLng pt2 = new LatLng(90,180); pts.add(pt2); LatLng pt3 = new LatLng(90,-180); pts.add(pt3); LatLng pt4 = new LatLng( getPoint(center,acc,0,r,half).latitude,-180); pts.add(pt4); for(int i=acc/2;i<acc+1;i++){ temp = getTemp(center,(2*Math.PI)/acc*i); // mR = getvertical(center,temp,r); mR = getmR(temp,r); mR = V3.add(mR,half); // System.out.println(mR.var1+"%%%%%%%"+mR.var2+"%%%%%%%%"+mR.var3); lat = getLatLng(mR); System.out.println(lat.latitude+"**********"+lat.longitude); pts.add(lat); } pt1 = new LatLng(lat.latitude,180); pts.add(pt1); pt2 = new LatLng(-90,180); pts.add(pt2); pt3 = new LatLng(-90,-180); pts.add(pt3); pt4 = new LatLng(getPoint(center,acc,acc/2,r,half).latitude,-180); pts.add(pt4); OverlayOptions polygonOption = new PolygonOptions() .points(pts) .stroke(new Stroke(4,0)) .fillColor(0x99999900); return polygonOption; } public static OverlayOptions hhh(){ List<LatLng> pts = new ArrayList<LatLng>(); LatLng p = new LatLng(20.0,30.0); pts.add(p); p = new LatLng(30.0,45.0); pts.add(p); p = new LatLng(40.0,20.0); pts.add(p); p = new LatLng(40.0,20.0); pts.add(p); p = new LatLng(40.0,20.0); pts.add(p); OverlayOptions polygonOption = new PolygonOptions() .points(pts) .stroke(new Stroke(4,0)) .fillColor(0x99999900); return polygonOption; } static class V3 { double var1 = 0.0; double var2 = 0.0; double var3 = 0.0; V3(){ } V3(Double var1,Double var2, Double var3){ this.var1 = var1; this.var2 = var2; this.var3 = var3; } public static V3 add(V3 v1, V3 v2){ return new V3(v1.var1+v2.var1,v1.var2+v2.var2,v1.var3+v2.var3); } } public static V3 getTemp(V3 center,Double ang){ center = getmR(center,1.0); Double x = center.var1; Double y = center.var2; Double z = center.var3; Matrix a = new Matrix(3,3); Matrix b = new Matrix(3,3); Matrix i = new Matrix(3,3); double[][] A = new double[3][3]; A[0][0] = x * x; A[0][1] = x * y; A[0][2] = x * z; A[1][0] = y * x; A[1][1] = y * y; A[1][2] = y * z; A[2][0] = z * x; A[2][1] = z * y; A[2][2] = z * z; double[][] B = new double[3][3]; B[0][0] = 0; B[0][1] = -z; B[0][2] = y; B[1][0] = z; B[1][1] = 0; B[1][2] = -x; B[2][0] = -y; B[2][1] = x; B[2][2] = 0; double[][] I = new double[3][3]; I[0][0] = 1; I[0][1] = 0; I[0][2] = 0; I[1][0] = 0; I[1][1] = 1; I[1][2] = 0; I[2][0] = 0; I[2][1] = 0; I[2][2] = 1; a.init(A); // System.out.println("A:"); // a.Output(); b.init(B); // System.out.println("B:"); // b.Output(); i.init(I); // System.out.println("I:"); // i.Output(); Matrix m = new Matrix(3,3); m = i.Minus(a); // m.Output(); m = m.mMultiply(Math.cos(ang)); // m.Output(); Matrix t = new Matrix(3,3); t = b; t = t.mMultiply(Math.sin(ang)); // m.Output(); m = m.Plus(t); // m.Output(); m = m.Plus(a); // m.Output(); m = m.Trans(); // m.Output(); t = new Matrix(3,1); double[][] T = new double[3][1]; // T[0][0] = 1.0;T[1][0] =1.0;//T[0][2] = -1*(center.var1*center.var2)/center.var3; // T[2][0] = -1*(center.var1*center.var2)/center.var3; T[0][0] = 1.0;T[1][0] =-1*center.var1/center.var2;T[2][0] =0.0; t.init(T); m = m.Multiply(t); m = m.Trans(); // m.Output(); return new V3(m.mat[0][0],m.mat[0][1],m.mat[0][2]); } public static V3 getvertical(V3 v1, V3 v2, Double r){ Double a1 = v1.var1; Double a2 = v1.var2; Double a3 = v1.var3; Double b1 = v2.var1; Double b2 = v2.var2; Double b3 = v2.var3; Double dy = (a1*b3-b1*a3)/(b2*a3-b3*a2); Double dz = (a1*b2-a2*b1)/(a2*b3-a3*b2); Double x = Math.sqrt((r*r)/(1+dy*dy+dz*dz)); if ((b2*a3-b3*a2) == 0){ x = 0.0; Double z = Math.sqrt((r*r)/((a3*a3)/(a2*a2)+1)); Double y = -1*(a3/a2)*z; if(x*a1+y*a2+z*a3 != 0||x*b1+y*b2+z*b3 != 0){ // System.out.println("ERROR!"); } return new V3(x,y,z); } if(x*a1+dy*x*a2+dz*x*a3 != 0||x*b1+dy*x*b2+dz*x*b3 != 0){ Double e1 = x*a1+dy*x*a2+dz*x*a3; Double e2 = x*b1+dy*x*b2+dz*x*b3; // System.out.println("ERROR! "+e1+" "+"ERROR! "+e2); } return new V3(x,dy*x,dz*x); } public static V3 getmR(V3 v,double r){ double x = v.var1; double y = v.var2; double z = v.var3; double sum = x*x + y*y + z*z; double k = Math.sqrt(sum/(r*r)); x = x/k; y = y/k; z = z/k; return new V3(x,y,z); } public static LatLng getPoint(V3 center,double acc,int i,double r,V3 half){ V3 temp = getTemp(center,(2*Math.PI)/acc*i); // mR = getvertical(center,temp,r); V3 mR = getmR(temp,r); // System.out.println(mR.var1+"%%%%%%%"+mR.var2+"%%%%%%%%"+mR.var3); mR = V3.add(mR,half); LatLng lat = getLatLng(mR); return lat; } }