Google Earth API - drawing lines that curve? - google-earth

I've been playing with the google earth API. I thought it would be neat to draw some lines between places from a relative 3D viewpoint. I've searched through the GE documentation and searched on google for answers but didn't find anything that led me down the correct path, so I thought I'd post some code and perhaps get some insight.
The following code plots two places and then draws a line between those places. Unfortunately the line that gets drawn splices the earth. Is there a method to make it wrap to the contour of the earth when drawn in 3D like this? I've attempted to vary the line height placement with a varying degree of success, but at the cost of accuracy and overall visual appeal when the line doesn't appear to connect the places.
function init() {
google.earth.createInstance('map3d', initCB, failureCB);
}
function initCB(instance) {
ge = instance;
ge.getWindow().setVisibility(true);
//---------------------------------PLACES
// Create the placemark.
var placemark = ge.createPlacemark('');
placemark.setName("Location 1");
// Set the placemark's location.
var point = ge.createPoint('');
point.setLatitude(39.96028);
point.setLongitude(-82.979736);
placemark.setGeometry(point);
// Add the placemark to Earth.
ge.getFeatures().appendChild(placemark);
// Create the placemark.
var placemark2 = ge.createPlacemark('');
placemark2.setName("Hop #2");
// Set the placemark's location.
var point2 = ge.createPoint('');
point2.setLatitude(25.7615);
point2.setLongitude(-80.2939);
placemark2.setGeometry(point2);
// Add the placemark to Earth.
ge.getFeatures().appendChild(placemark2);
//---------------------------------FOCUS
var lookAt = ge.createLookAt('');
lookAt.setLatitude(39.96028);
lookAt.setLongitude(-82.979736);
lookAt.setRange(1000000.0);
lookAt.setAltitude(0);
lookAt.setTilt(45);
ge.getView().setAbstractView(lookAt);
//---------------------------------LINES
// Create the placemark
var lineStringPlacemark = ge.createPlacemark('');
// Create the LineString
var lineString = ge.createLineString('');
lineStringPlacemark.setGeometry(lineString);
// Add LineString points
lineString.getCoordinates().pushLatLngAlt(39.96028, -82.979736, 0);
lineString.getCoordinates().pushLatLngAlt(25.7615, -80.2939, 0);
//lineString.setAltitudeMode(ge.ALTITUDE_CLAMP_TO_GROUND);
//lineString.setAltitudeMode(ge.ALTITUDE_RELATIVE_TO_GROUND);
lineString.setAltitudeMode(ge.absolute);
// Create a style and set width and color of line
lineStringPlacemark.setStyleSelector(ge.createStyle(''));
var lineStyle = lineStringPlacemark.getStyleSelector().getLineStyle();
lineStyle.setWidth(2);
lineStyle.getColor().set('9900ffff'); // aabbggrr format
// Add the feature to Earth
ge.getFeatures().appendChild(lineStringPlacemark);
}
function failureCB(errorCode) {
}
google.setOnLoadCallback(init);

You will want to set tesselation, and optionally extrude, on your linestring to true.
See https://developers.google.com/kml/documentation/kmlreference#tessellate and https://developers.google.com/kml/documentation/kmlreference#extrude for details
For the API, your syntax would be something like
lineStringPlacemark.setTessellate(true);
lineStringPlacemark.setExtrude(true);
There's some additional API examples on this at https://developers.google.com/earth/documentation/geometries

Related

Not understanding how to use the return value of leaflet getCenter()

I'm trying to put a marker at the center point of these coordinates. After calculating the bounds, I use getCenter() to give me the coordinates. But it returns "LatLng(-93.20448, 38.902475)" in longitude latitude direction, so using them in the marker doesn't work. It needs lat lon. I realize I'm not understanding something but what is it that I don't understand.
var bounds = L.latLngBounds([[-94.778092, 39.967458], [-91.630869, 39.967458], [-91.630869, 37.8374921], [-94.778092, 37.8374921], [-94.778092, 39.967458]]);
var middle = bounds.getCenter(); alert(middle); // LatLng(-93.20448, 38.902475)
var mk5 = new L.marker(new L.latLng( 38.902475, -93.20448 );

How to figure out resolution array from user inputted ArcGIS projection data

I have a Leaflet based mapping solution that uses ArcGIS map configuration supplied by a user (I have no idea what it will be, they will customize it with their own ArcGIS services). The issue is that the projection can be pretty much anything, and I will need to use Proj4Leaflet to configure the CRS of the map accordingly. The problem I'm running into is I'm not sure how to calculate the scale/resolution array. The user is inputting these values: projection key, Proj4 string, origin, bounds, zoom levels.
So, for example (yes I know EPSG:3857 is standard and I could just use L.CRS.EPSG3857 but it serves as a good example of how to set the same thing up using Proj4Leaflet):
Projection key = EPSG:3857
Proj4 string = +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=#null +wktext +no_defs
Origin = [0,0]
Bounds = [[-20026376.39, -20048966.10], [20026376.39, 20048966.10]]
Zoom levels = 18
With that I think I have enough to set up a L.Proj.CRS for it:
var crs = new L.Proj.CRS("EPSG:3857", "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=#null +wktext +no_defs",
{
resolutions : [?????],
origin : [0,0],
bounds : [[-20026376.39, -20048966.10], [20026376.39, 20048966.10]]
});
I have everything I need apart from the resolutions array, I am not sure exactly how to go about setting that up based on the data given and having a hard time finding answers to get me pointed in the right direction.
So bottom line, the only way I found to calculate resolutions is if it is a mercator projection and we know the longitude extents of it and the tile size. Otherwise the resolutions will need to be looked up at the ArcGIS Server tile server REST endpoint. Thus for my project I will need the user to supply the array themselves and cannot calculate it for them.
In the case of the mercator projection, I came up with this function that does the trick:
function parseResolutionsFromZoomLevels(zoomLevels, tileSize, mapWGS84Extent)
{
var metersPerExtent = 40075008/360;
var mapWGS84Meters = mapWGS84Extent*metersPerExtent;
var resolutionArray = [];
for (var i=0; i<zoomLevels; i++)
{
var tilesAtZoom = Math.pow(2,i);
var pixelsAtZoom = tilesAtZoom*tileSize;
resolutionArray.push(mapWGS84Meters/pixelsAtZoom);
}
return resolutionArray;
}
Hope this helps anyone else that happens to encounter this same situation.

Bing Maps V8 SDK - Get drawn shapes

I have a Bing map with drawing manager enabled for users to draw shapes (mostly one polygon at a time). I want to be able to get the details of the drawn polygon so I can save it in the database.
The below function can access the shapes but returns the coordinates only
function getShapes()
{
var shapes = drawingManager.getPrimitives();
if (shapes && shapes.length > 0)
{
var rings = shapes[0].getRings();
alert('Retrieved ' + rings[0] + ' from the drawing manager.');
}
else
{
alert('No shapes in the drawing manager.');
}
}
result is:
Retrieved [MapLocation (35.17314901376581, 44.72432011035158)],[MapLocation (35.10324034213123, 44.73015659716798)],[MapLocation (35.12346106720259, 44.90525120166017)],[MapLocation (35.18633788986748, 44.88362186816408)],[MapLocation (35.17314901376581, 44.72432011035158)] from the drawing manager.
How can I get the exact drawn shape details not just the coordinates?
Remove getRings() and you will have the shape object. The Get Rings function retudrrns the coordinates of a polygon.

Find markers shown in mapView

I am trying to determine the marker/s currently shown in a mapview. I have researched the following the method:
self.mapView.bounds.contains(markers[0].position)
But the contains command accepts CGPoint or CGRect. In other platforms except Swift, contains can accept the marker's position.
How do I convert the marker's position to be accepted by contains?
Use the mapview's current projection. Use the projection's method called containsCoordinate to check if you marker's position is inside the projection, i.e. currently visible.
So something like:
let coord = marker.position
let isVisible = self.mapview.projection.containsCoordinate(coord)
https://developers.google.com/maps/documentation/ios-sdk/reference/interface_g_m_s_projection.html#aa6ad29e6831e40f00435c3aaeda8e08a
In MapKit:
I assume with Marker you mean an MKAnnotation. Instead of using the bounds of the mapView you should use the visibleMapRect and see if it contains the coordinates of the Marker in MKMapPoints. This is the code I used:
let markerPoint = MKMapPointForCoordinate(markers[0].coordinate)
if MKMapRectContainsPoint(mapView.visibleMapRect, markerPoint) {
print("Found")
} else {
print("Not found")
}
Only when the coordinates of the markers are visible (in other words, the marker is being displayed), this will print "Found". If off-screen, it will print "Not found".

Check if coordinates are inside a KML polygon

I have a KML file which defines a region (or polygon). I would like to make a function that checks if a given coordinate is inside or outside that polygon.
This is the KML if you want to take a look: http://pastebin.com/LGfn3L8H
I don't want to show any map, I just want to return a boolean.
Point-in-polygon (PiP) is a very well-studied computational geometry problem, so there are lots of algorithms and implementations out there that you can use. Searching SO will probably find several you can copy-paste, even.
There's a catch, though—you're dealing with polygons on the surface of the Earth... which is a sphere, not the infinite Euclidean plane that most PiP algorithms expect to work with. (You can, for example, have triangles whose internal angles add up to greater than π radians.) So naively deploying a PiP algorithm will give you incorrect answers for edge cases.
It's probably easiest to use a library that can account for differences between Euclidean and spherical (or, more precisely, Earth-shaped) geometry—that is, a mapping library like MapKit. There are tricks like the one in this SO answer that let you convert a MKPolygon to a CGPath through map projection, after which you can use the CGPathContainsPoint function to test against the flat 2D polygon corresponding to your Earth-surface polygon.
Of course, to do that you'll also need to get your KML file imported to MapKit. Apple has a sample code project illustrating how to do this.
This can be done with GMSGeometryContainsLocation.
I wrote a method that makes use of the GoogleMapsUtils library's GMUKMLParser.
func findPolygonName(_ location: CLLocationCoordinate2D) {
var name: String?
outerLoop: for placemark in kmlParser.placemarks {
if let polygon = (placemark as? GMUPlacemark)?.geometry as? GMUPolygon {
for path in polygon.paths {
if GMSGeometryContainsLocation(location, path, true) {
name = (placemark as? GMUPlacemark)?.title
break outerLoop
}
}
}
}
if let n = name, !n.isEmpty {
locationLabel.text = n
} else {
locationLabel.text = "We do not deliver here"
}
}
This function iterates over the polygon and its path to determine whether the given coordinates are within the path.