mirror of https://github.com/apache/lucene.git
LUCENE-7189: make it easier to write WebGL earth HTML for debugging geo failures
This commit is contained in:
parent
f7a25f07ad
commit
347b9f0d7f
|
@ -308,7 +308,10 @@ public class TestGeoUtils extends LuceneTestCase {
|
||||||
centerLat, centerLon, lat, lon, distance, Rectangle.fromPointDistance(centerLat, centerLon, radius)),
|
centerLat, centerLon, lat, lon, distance, Rectangle.fromPointDistance(centerLat, centerLon, radius)),
|
||||||
distance > radius);
|
distance > radius);
|
||||||
} catch (AssertionError e) {
|
} catch (AssertionError e) {
|
||||||
GeoTestUtil.toWebGLEarth(latMin, latMax, lonMin, lonMax, centerLat, centerLon, radius);
|
EarthDebugger ed = new EarthDebugger();
|
||||||
|
ed.addRect(latMin, latMax, lonMin, lonMax);
|
||||||
|
ed.addCircle(centerLat, centerLon, radius, true);
|
||||||
|
System.out.println(ed.finish());
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,267 @@
|
||||||
|
package org.apache.lucene.geo;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import org.apache.lucene.geo.Polygon;
|
||||||
|
import org.apache.lucene.geo.Rectangle;
|
||||||
|
import org.apache.lucene.util.SloppyMath;
|
||||||
|
|
||||||
|
/** Draws shapes on the earth surface and renders using the very cool http://www.webglearth.org.
|
||||||
|
*
|
||||||
|
* Just instantiate this class, add the things you want plotted, and call {@link #finish} to get the
|
||||||
|
* resulting HTML that you should save and load with a browser. */
|
||||||
|
|
||||||
|
public class EarthDebugger {
|
||||||
|
final StringBuilder b = new StringBuilder();
|
||||||
|
private int nextShape;
|
||||||
|
|
||||||
|
public EarthDebugger() {
|
||||||
|
b.append("<!DOCTYPE HTML>\n");
|
||||||
|
b.append("<html>\n");
|
||||||
|
b.append(" <head>\n");
|
||||||
|
b.append(" <script src=\"http://www.webglearth.com/v2/api.js\"></script>\n");
|
||||||
|
b.append(" <script>\n");
|
||||||
|
b.append(" function initialize() {\n");
|
||||||
|
b.append(" var earth = new WE.map('earth_div');\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPolygon(Polygon poly) {
|
||||||
|
addPolygon(poly, "#00ff00");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPolygon(Polygon poly, String color) {
|
||||||
|
String name = "poly" + nextShape;
|
||||||
|
nextShape++;
|
||||||
|
|
||||||
|
b.append(" var " + name + " = WE.polygon([\n");
|
||||||
|
double[] polyLats = poly.getPolyLats();
|
||||||
|
double[] polyLons = poly.getPolyLons();
|
||||||
|
for(int i=0;i<polyLats.length;i++) {
|
||||||
|
b.append(" [" + polyLats[i] + ", " + polyLons[i] + "],\n");
|
||||||
|
}
|
||||||
|
b.append(" ], {color: '" + color + "'});\n");
|
||||||
|
b.append(" " + name + ".addTo(earth);\n");
|
||||||
|
|
||||||
|
for (Polygon hole : poly.getHoles()) {
|
||||||
|
addPolygon(poly, "#ffffff");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// first point is inclusive, last point is exclusive!
|
||||||
|
private void drawSegment(double minLat, double maxLat, double minLon, double maxLon) {
|
||||||
|
int steps = 20;
|
||||||
|
for(int i=0;i<steps;i++) {
|
||||||
|
b.append(" [" + (minLat + (maxLat - minLat) * i / steps) + ", " + (minLon + (maxLon - minLon) * i / steps) + "],\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addRect(double minLat, double maxLat, double minLon, double maxLon) {
|
||||||
|
int steps = 20;
|
||||||
|
String name = "rect" + nextShape;
|
||||||
|
nextShape++;
|
||||||
|
|
||||||
|
b.append(" var " + name + " = WE.polygon([\n");
|
||||||
|
|
||||||
|
b.append(" // min -> max lat, min lon\n");
|
||||||
|
drawSegment(minLat, maxLat, minLon, minLon);
|
||||||
|
|
||||||
|
b.append(" // max lat, min -> max lon\n");
|
||||||
|
drawSegment(maxLat, maxLat, minLon, maxLon);
|
||||||
|
|
||||||
|
b.append(" // max -> min lat, max lon\n");
|
||||||
|
drawSegment(maxLat, minLat, maxLon, maxLon);
|
||||||
|
|
||||||
|
b.append(" // min lat, max -> min lon\n");
|
||||||
|
drawSegment(minLat, minLat, maxLon, minLon);
|
||||||
|
|
||||||
|
b.append(" // min lat, min lon\n");
|
||||||
|
b.append(" [" + minLat + ", " + minLon + "]\n");
|
||||||
|
b.append(" ], {color: \"#ff0000\", fillColor: \"#ff0000\"});\n");
|
||||||
|
b.append(" " + name + ".addTo(earth);\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Draws a line a fixed latitude, spanning the min/max longitude */
|
||||||
|
public void addLatLine(double lat, double minLon, double maxLon) {
|
||||||
|
String name = "latline" + nextShape;
|
||||||
|
nextShape++;
|
||||||
|
|
||||||
|
b.append(" var " + name + " = WE.polygon([\n");
|
||||||
|
double lon;
|
||||||
|
int steps = 36;
|
||||||
|
for(lon = minLon;lon<=maxLon;lon += (maxLon-minLon)/steps) {
|
||||||
|
b.append(" [" + lat + ", " + lon + "],\n");
|
||||||
|
}
|
||||||
|
b.append(" [" + lat + ", " + maxLon + "],\n");
|
||||||
|
lon -= (maxLon-minLon)/steps;
|
||||||
|
for(;lon>=minLon;lon -= (maxLon-minLon)/steps) {
|
||||||
|
b.append(" [" + lat + ", " + lon + "],\n");
|
||||||
|
}
|
||||||
|
b.append(" ], {color: \"#ff0000\", fillColor: \"#ffffff\", opacity: 1, fillOpacity: 0.0001});\n");
|
||||||
|
b.append(" " + name + ".addTo(earth);\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Draws a line a fixed longitude, spanning the min/max latitude */
|
||||||
|
public void addLonLine(double minLat, double maxLat, double lon) {
|
||||||
|
String name = "lonline" + nextShape;
|
||||||
|
nextShape++;
|
||||||
|
|
||||||
|
b.append(" var " + name + " = WE.polygon([\n");
|
||||||
|
double lat;
|
||||||
|
int steps = 36;
|
||||||
|
for(lat = minLat;lat<=maxLat;lat += (maxLat-minLat)/steps) {
|
||||||
|
b.append(" [" + lat + ", " + lon + "],\n");
|
||||||
|
}
|
||||||
|
b.append(" [" + maxLat + ", " + lon + "],\n");
|
||||||
|
lat -= (maxLat-minLat)/36;
|
||||||
|
for(;lat>=minLat;lat -= (maxLat-minLat)/steps) {
|
||||||
|
b.append(" [" + lat + ", " + lon + "],\n");
|
||||||
|
}
|
||||||
|
b.append(" ], {color: \"#ff0000\", fillColor: \"#ffffff\", opacity: 1, fillOpacity: 0.0001});\n");
|
||||||
|
b.append(" " + name + ".addTo(earth);\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPoint(double lat, double lon) {
|
||||||
|
b.append(" WE.marker([" + lat + ", " + lon + "]).addTo(earth);\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addCircle(double centerLat, double centerLon, double radiusMeters, boolean alsoAddBBox) {
|
||||||
|
addPoint(centerLat, centerLon);
|
||||||
|
String name = "circle" + nextShape;
|
||||||
|
nextShape++;
|
||||||
|
b.append(" var " + name + " = WE.polygon([\n");
|
||||||
|
inverseHaversin(b, centerLat, centerLon, radiusMeters);
|
||||||
|
b.append(" ], {color: '#00ff00'});\n");
|
||||||
|
b.append(" " + name + ".addTo(earth);\n");
|
||||||
|
|
||||||
|
if (alsoAddBBox) {
|
||||||
|
Rectangle box = Rectangle.fromPointDistance(centerLat, centerLon, radiusMeters);
|
||||||
|
addRect(box.minLat, box.maxLat, box.minLon, box.maxLon);
|
||||||
|
addLatLine(Rectangle.axisLat(centerLat, radiusMeters), box.minLon, box.maxLon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String finish() {
|
||||||
|
b.append(" WE.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',{\n");
|
||||||
|
b.append(" attribution: '© OpenStreetMap contributors'\n");
|
||||||
|
b.append(" }).addTo(earth);\n");
|
||||||
|
b.append(" }\n");
|
||||||
|
b.append(" </script>\n");
|
||||||
|
b.append(" <style>\n");
|
||||||
|
b.append(" html, body{padding: 0; margin: 0;}\n");
|
||||||
|
b.append(" #earth_div{top: 0; right: 0; bottom: 0; left: 0; position: absolute !important;}\n");
|
||||||
|
b.append(" </style>\n");
|
||||||
|
b.append(" <title>WebGL Earth API: Hello World</title>\n");
|
||||||
|
b.append(" </head>\n");
|
||||||
|
b.append(" <body onload=\"initialize()\">\n");
|
||||||
|
b.append(" <div id=\"earth_div\"></div>\n");
|
||||||
|
b.append(" </body>\n");
|
||||||
|
b.append("</html>\n");
|
||||||
|
|
||||||
|
return b.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void inverseHaversin(StringBuilder b, double centerLat, double centerLon, double radiusMeters) {
|
||||||
|
double angle = 0;
|
||||||
|
int steps = 100;
|
||||||
|
|
||||||
|
newAngle:
|
||||||
|
while (angle < 360) {
|
||||||
|
double x = Math.cos(Math.toRadians(angle));
|
||||||
|
double y = Math.sin(Math.toRadians(angle));
|
||||||
|
double factor = 2.0;
|
||||||
|
double step = 1.0;
|
||||||
|
int last = 0;
|
||||||
|
double lastDistanceMeters = 0.0;
|
||||||
|
//System.out.println("angle " + angle + " slope=" + slope);
|
||||||
|
while (true) {
|
||||||
|
double lat = wrapLat(centerLat + y * factor);
|
||||||
|
double lon = wrapLon(centerLon + x * factor);
|
||||||
|
double distanceMeters = SloppyMath.haversinMeters(centerLat, centerLon, lat, lon);
|
||||||
|
|
||||||
|
if (last == 1 && distanceMeters < lastDistanceMeters) {
|
||||||
|
// For large enough circles, some angles are not possible:
|
||||||
|
//System.out.println(" done: give up on angle " + angle);
|
||||||
|
angle += 360./steps;
|
||||||
|
continue newAngle;
|
||||||
|
}
|
||||||
|
if (last == -1 && distanceMeters > lastDistanceMeters) {
|
||||||
|
// For large enough circles, some angles are not possible:
|
||||||
|
//System.out.println(" done: give up on angle " + angle);
|
||||||
|
angle += 360./steps;
|
||||||
|
continue newAngle;
|
||||||
|
}
|
||||||
|
lastDistanceMeters = distanceMeters;
|
||||||
|
|
||||||
|
//System.out.println(" iter lat=" + lat + " lon=" + lon + " distance=" + distanceMeters + " vs " + radiusMeters);
|
||||||
|
if (Math.abs(distanceMeters - radiusMeters) < 0.1) {
|
||||||
|
b.append(" [" + lat + ", " + lon + "],\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (distanceMeters > radiusMeters) {
|
||||||
|
// too big
|
||||||
|
//System.out.println(" smaller");
|
||||||
|
factor -= step;
|
||||||
|
if (last == 1) {
|
||||||
|
//System.out.println(" half-step");
|
||||||
|
step /= 2.0;
|
||||||
|
}
|
||||||
|
last = -1;
|
||||||
|
} else if (distanceMeters < radiusMeters) {
|
||||||
|
// too small
|
||||||
|
//System.out.println(" bigger");
|
||||||
|
factor += step;
|
||||||
|
if (last == -1) {
|
||||||
|
//System.out.println(" half-step");
|
||||||
|
step /= 2.0;
|
||||||
|
}
|
||||||
|
last = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
angle += 360./steps;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// craziness for plotting stuff :)
|
||||||
|
|
||||||
|
private static double wrapLat(double lat) {
|
||||||
|
//System.out.println("wrapLat " + lat);
|
||||||
|
if (lat > 90) {
|
||||||
|
//System.out.println(" " + (180 - lat));
|
||||||
|
return 180 - lat;
|
||||||
|
} else if (lat < -90) {
|
||||||
|
//System.out.println(" " + (-180 - lat));
|
||||||
|
return -180 - lat;
|
||||||
|
} else {
|
||||||
|
//System.out.println(" " + lat);
|
||||||
|
return lat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double wrapLon(double lon) {
|
||||||
|
//System.out.println("wrapLon " + lon);
|
||||||
|
if (lon > 180) {
|
||||||
|
//System.out.println(" " + (lon - 360));
|
||||||
|
return lon - 360;
|
||||||
|
} else if (lon < -180) {
|
||||||
|
//System.out.println(" " + (lon + 360));
|
||||||
|
return lon + 360;
|
||||||
|
} else {
|
||||||
|
//System.out.println(" " + lon);
|
||||||
|
return lon;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -281,232 +281,4 @@ public class GeoTestUtil {
|
||||||
private static Random random() {
|
private static Random random() {
|
||||||
return RandomizedContext.current().getRandom();
|
return RandomizedContext.current().getRandom();
|
||||||
}
|
}
|
||||||
|
|
||||||
// craziness for plotting stuff :)
|
|
||||||
|
|
||||||
private static double wrapLat(double lat) {
|
|
||||||
//System.out.println("wrapLat " + lat);
|
|
||||||
if (lat > 90) {
|
|
||||||
//System.out.println(" " + (180 - lat));
|
|
||||||
return 180 - lat;
|
|
||||||
} else if (lat < -90) {
|
|
||||||
//System.out.println(" " + (-180 - lat));
|
|
||||||
return -180 - lat;
|
|
||||||
} else {
|
|
||||||
//System.out.println(" " + lat);
|
|
||||||
return lat;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static double wrapLon(double lon) {
|
|
||||||
//System.out.println("wrapLon " + lon);
|
|
||||||
if (lon > 180) {
|
|
||||||
//System.out.println(" " + (lon - 360));
|
|
||||||
return lon - 360;
|
|
||||||
} else if (lon < -180) {
|
|
||||||
//System.out.println(" " + (lon + 360));
|
|
||||||
return lon + 360;
|
|
||||||
} else {
|
|
||||||
//System.out.println(" " + lon);
|
|
||||||
return lon;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void drawRectApproximatelyOnEarthSurface(String name, String color, double minLat, double maxLat, double minLon, double maxLon) {
|
|
||||||
int steps = 20;
|
|
||||||
System.out.println(" var " + name + " = WE.polygon([");
|
|
||||||
System.out.println(" // min -> max lat, min lon");
|
|
||||||
for(int i=0;i<steps;i++) {
|
|
||||||
System.out.println(" [" + (minLat + (maxLat - minLat) * i / steps) + ", " + minLon + "],");
|
|
||||||
}
|
|
||||||
System.out.println(" // max lat, min -> max lon");
|
|
||||||
for(int i=0;i<steps;i++) {
|
|
||||||
System.out.println(" [" + (maxLat + ", " + (minLon + (maxLon - minLon) * i / steps)) + "],");
|
|
||||||
}
|
|
||||||
System.out.println(" // max -> min lat, max lon");
|
|
||||||
for(int i=0;i<steps;i++) {
|
|
||||||
System.out.println(" [" + (minLat + (maxLat - minLat) * (steps-i) / steps) + ", " + maxLon + "],");
|
|
||||||
}
|
|
||||||
System.out.println(" // min lat, max -> min lon");
|
|
||||||
for(int i=0;i<steps;i++) {
|
|
||||||
System.out.println(" [" + minLat + ", " + (minLon + (maxLon - minLon) * (steps-i) / steps) + "],");
|
|
||||||
}
|
|
||||||
System.out.println(" // min lat, min lon");
|
|
||||||
System.out.println(" [" + minLat + ", " + minLon + "]");
|
|
||||||
System.out.println(" ], {color: \"" + color + "\", fillColor: \"" + color + "\"});");
|
|
||||||
System.out.println(" " + name + ".addTo(earth);");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void plotLatApproximatelyOnEarthSurface(String name, String color, double lat, double minLon, double maxLon) {
|
|
||||||
System.out.println(" var " + name + " = WE.polygon([");
|
|
||||||
double lon;
|
|
||||||
for(lon = minLon;lon<=maxLon;lon += (maxLon-minLon)/36) {
|
|
||||||
System.out.println(" [" + lat + ", " + lon + "],");
|
|
||||||
}
|
|
||||||
System.out.println(" [" + lat + ", " + maxLon + "],");
|
|
||||||
lon -= (maxLon-minLon)/36;
|
|
||||||
for(;lon>=minLon;lon -= (maxLon-minLon)/36) {
|
|
||||||
System.out.println(" [" + lat + ", " + lon + "],");
|
|
||||||
}
|
|
||||||
System.out.println(" ], {color: \"" + color + "\", fillColor: \"#ffffff\", opacity: " + (color.equals("#ffffff") ? "0.3" : "1") + ", fillOpacity: 0.0001});");
|
|
||||||
System.out.println(" " + name + ".addTo(earth);");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void plotLonApproximatelyOnEarthSurface(String name, String color, double lon, double minLat, double maxLat) {
|
|
||||||
System.out.println(" var " + name + " = WE.polygon([");
|
|
||||||
double lat;
|
|
||||||
for(lat = minLat;lat<=maxLat;lat += (maxLat-minLat)/36) {
|
|
||||||
System.out.println(" [" + lat + ", " + lon + "],");
|
|
||||||
}
|
|
||||||
System.out.println(" [" + maxLat + ", " + lon + "],");
|
|
||||||
lat -= (maxLat-minLat)/36;
|
|
||||||
for(;lat>=minLat;lat -= (maxLat-minLat)/36) {
|
|
||||||
System.out.println(" [" + lat + ", " + lon + "],");
|
|
||||||
}
|
|
||||||
System.out.println(" ], {color: \"" + color + "\", fillColor: \"#ffffff\", opacity: " + (color.equals("#ffffff") ? "0.3" : "1") + ", fillOpacity: 0.0001});");
|
|
||||||
System.out.println(" " + name + ".addTo(earth);");
|
|
||||||
}
|
|
||||||
|
|
||||||
// http://www.webglearth.org has API details:
|
|
||||||
public static void polysToWebGLEarth(List<double[][]> polys) {
|
|
||||||
System.out.println("<!DOCTYPE HTML>");
|
|
||||||
System.out.println("<html>");
|
|
||||||
System.out.println(" <head>");
|
|
||||||
System.out.println(" <script src=\"http://www.webglearth.com/v2/api.js\"></script>");
|
|
||||||
System.out.println(" <script>");
|
|
||||||
System.out.println(" function initialize() {");
|
|
||||||
System.out.println(" var earth = new WE.map('earth_div');");
|
|
||||||
|
|
||||||
int count = 0;
|
|
||||||
for (double[][] poly : polys) {
|
|
||||||
System.out.println(" var poly" + count + " = WE.polygon([");
|
|
||||||
for(int i=0;i<poly[0].length;i++) {
|
|
||||||
double lat = poly[0][i];
|
|
||||||
double lon = poly[1][i];
|
|
||||||
System.out.println(" [" + lat + ", " + lon + "],");
|
|
||||||
}
|
|
||||||
System.out.println(" ], {color: '#00ff00'});");
|
|
||||||
System.out.println(" poly" + count + ".addTo(earth);");
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println(" WE.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',{");
|
|
||||||
System.out.println(" attribution: '© OpenStreetMap contributors'");
|
|
||||||
System.out.println(" }).addTo(earth);");
|
|
||||||
System.out.println(" }");
|
|
||||||
System.out.println(" </script>");
|
|
||||||
System.out.println(" <style>");
|
|
||||||
System.out.println(" html, body{padding: 0; margin: 0;}");
|
|
||||||
System.out.println(" #earth_div{top: 0; right: 0; bottom: 0; left: 0; position: absolute !important;}");
|
|
||||||
System.out.println(" </style>");
|
|
||||||
System.out.println(" <title>WebGL Earth API: Hello World</title>");
|
|
||||||
System.out.println(" </head>");
|
|
||||||
System.out.println(" <body onload=\"initialize()\">");
|
|
||||||
System.out.println(" <div id=\"earth_div\"></div>");
|
|
||||||
System.out.println(" </body>");
|
|
||||||
System.out.println("</html>");
|
|
||||||
}
|
|
||||||
|
|
||||||
// http://www.webglearth.org has API details:
|
|
||||||
public static void toWebGLEarth(double rectMinLatitude, double rectMaxLatitude,
|
|
||||||
double rectMinLongitude, double rectMaxLongitude,
|
|
||||||
double centerLatitude, double centerLongitude,
|
|
||||||
double radiusMeters) {
|
|
||||||
Rectangle box = Rectangle.fromPointDistance(centerLatitude, centerLongitude, radiusMeters);
|
|
||||||
System.out.println("<!DOCTYPE HTML>");
|
|
||||||
System.out.println("<html>");
|
|
||||||
System.out.println(" <head>");
|
|
||||||
System.out.println(" <script src=\"http://www.webglearth.com/v2/api.js\"></script>");
|
|
||||||
System.out.println(" <script>");
|
|
||||||
System.out.println(" function initialize() {");
|
|
||||||
System.out.println(" var earth = new WE.map('earth_div', {center: [" + centerLatitude + ", " + centerLongitude + "]});");
|
|
||||||
System.out.println(" var marker = WE.marker([" + centerLatitude + ", " + centerLongitude + "]).addTo(earth);");
|
|
||||||
drawRectApproximatelyOnEarthSurface("cell", "#ff0000", rectMinLatitude, rectMaxLatitude, rectMinLongitude, rectMaxLongitude);
|
|
||||||
System.out.println(" var polygonB = WE.polygon([");
|
|
||||||
StringBuilder b = new StringBuilder();
|
|
||||||
inverseHaversin(b, centerLatitude, centerLongitude, radiusMeters);
|
|
||||||
System.out.println(b);
|
|
||||||
System.out.println(" ], {color: '#00ff00'});");
|
|
||||||
System.out.println(" polygonB.addTo(earth);");
|
|
||||||
drawRectApproximatelyOnEarthSurface("bbox", "#00ff00", box.minLat, box.maxLat, box.minLon, box.maxLon);
|
|
||||||
System.out.println(" WE.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',{");
|
|
||||||
System.out.println(" attribution: '© OpenStreetMap contributors'");
|
|
||||||
System.out.println(" }).addTo(earth);");
|
|
||||||
plotLatApproximatelyOnEarthSurface("lat0", "#ffffff", 4.68, 0.0, 360.0);
|
|
||||||
plotLatApproximatelyOnEarthSurface("lat1", "#ffffff", 180-93.09, 0.0, 360.0);
|
|
||||||
plotLatApproximatelyOnEarthSurface("axisLat", "#00ff00", Rectangle.axisLat(centerLatitude, radiusMeters), box.minLon, box.maxLon);
|
|
||||||
plotLonApproximatelyOnEarthSurface("axisLon", "#00ff00", centerLongitude, box.minLat, box.maxLat);
|
|
||||||
System.out.println(" }");
|
|
||||||
System.out.println(" </script>");
|
|
||||||
System.out.println(" <style>");
|
|
||||||
System.out.println(" html, body{padding: 0; margin: 0;}");
|
|
||||||
System.out.println(" #earth_div{top: 0; right: 0; bottom: 0; left: 0; position: absolute !important;}");
|
|
||||||
System.out.println(" </style>");
|
|
||||||
System.out.println(" <title>WebGL Earth API: Hello World</title>");
|
|
||||||
System.out.println(" </head>");
|
|
||||||
System.out.println(" <body onload=\"initialize()\">");
|
|
||||||
System.out.println(" <div id=\"earth_div\"></div>");
|
|
||||||
System.out.println(" </body>");
|
|
||||||
System.out.println("</html>");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void inverseHaversin(StringBuilder b, double centerLat, double centerLon, double radiusMeters) {
|
|
||||||
double angle = 0;
|
|
||||||
int steps = 100;
|
|
||||||
|
|
||||||
newAngle:
|
|
||||||
while (angle < 360) {
|
|
||||||
double x = Math.cos(Math.toRadians(angle));
|
|
||||||
double y = Math.sin(Math.toRadians(angle));
|
|
||||||
double factor = 2.0;
|
|
||||||
double step = 1.0;
|
|
||||||
int last = 0;
|
|
||||||
double lastDistanceMeters = 0.0;
|
|
||||||
//System.out.println("angle " + angle + " slope=" + slope);
|
|
||||||
while (true) {
|
|
||||||
double lat = wrapLat(centerLat + y * factor);
|
|
||||||
double lon = wrapLon(centerLon + x * factor);
|
|
||||||
double distanceMeters = SloppyMath.haversinMeters(centerLat, centerLon, lat, lon);
|
|
||||||
|
|
||||||
if (last == 1 && distanceMeters < lastDistanceMeters) {
|
|
||||||
// For large enough circles, some angles are not possible:
|
|
||||||
//System.out.println(" done: give up on angle " + angle);
|
|
||||||
angle += 360./steps;
|
|
||||||
continue newAngle;
|
|
||||||
}
|
|
||||||
if (last == -1 && distanceMeters > lastDistanceMeters) {
|
|
||||||
// For large enough circles, some angles are not possible:
|
|
||||||
//System.out.println(" done: give up on angle " + angle);
|
|
||||||
angle += 360./steps;
|
|
||||||
continue newAngle;
|
|
||||||
}
|
|
||||||
lastDistanceMeters = distanceMeters;
|
|
||||||
|
|
||||||
//System.out.println(" iter lat=" + lat + " lon=" + lon + " distance=" + distanceMeters + " vs " + radiusMeters);
|
|
||||||
if (Math.abs(distanceMeters - radiusMeters) < 0.1) {
|
|
||||||
b.append(" [" + lat + ", " + lon + "],\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (distanceMeters > radiusMeters) {
|
|
||||||
// too big
|
|
||||||
//System.out.println(" smaller");
|
|
||||||
factor -= step;
|
|
||||||
if (last == 1) {
|
|
||||||
//System.out.println(" half-step");
|
|
||||||
step /= 2.0;
|
|
||||||
}
|
|
||||||
last = -1;
|
|
||||||
} else if (distanceMeters < radiusMeters) {
|
|
||||||
// too small
|
|
||||||
//System.out.println(" bigger");
|
|
||||||
factor += step;
|
|
||||||
if (last == -1) {
|
|
||||||
//System.out.println(" half-step");
|
|
||||||
step /= 2.0;
|
|
||||||
}
|
|
||||||
last = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
angle += 360./steps;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue