Longitude, Latitude to XY coordinate conversion - iphone

I want to now how to convert longitude, latitude to its equivalent xy coordinate components in iPhone programming. (I am using only CoreLocation programming, and want to show a point on iPhone screen without any map).
thanks

Well the exact conversion depends on exactly which part of the Earth you want to show, and the stretching along longitude varies according to latitude, at least in Mercator.
That being said, even if you don't want to display an actual MapKit map, it would probably be easiest to create an MKMapView and keep it to one side. If you set the area you want to display appropriately on that (by setting the region property), you can use convertCoordinate:toPointToView: to map from longitude and latitude to a 2d screen location.
Note that MKMapView adjusts the region you set so as to make sense for the viewport its been given (eg, if you gave it a region that was a short fat rectangle, but the view it had was a tall thin rectangle, it'd pick the smallest region that covers the entire short fat rectangle but is the shape of a tall thin rectangle), so don't get confused if you specify a region with the top left being a particular geolocation, but then that geolocation isn't at the exact top left of the view.

Related

Mapbox Unity SDK: storying and displaying short relative distances

I was wondering how I can go about storing and displaying small, but geographically accurate distances in the mapbox unity SDK?
I'm storing radius' about markers on a map, I get the value in meters (from ~0.5m-10m), and then, adaptively with the zoom level, I want to accurately display those meters in Unity world space (draw an ellipse) using these stored values. The problem is that the mapbox api from my understanding only lets you to convert lat/long to unity world coordinates and I'm running into precision errors. I can get adequate precision when using the CheapRuler class and meters, but as soon as I use the _map.GeoToWorld(latlon) method the precision is lost.
How would I go about keeping adequate precession, is there a way I can use the marker as the reference point and the radius as the offset, and get the relative unity world coordinate distance (of the radius) that way? I know you can also store scale relative to the mapbox tiles, but I'm not sure how I can convert that back to a unity world distance. I'm operating on very small distances, so any warping due to lat/long being a Mercator projection can probably be ignored.
I figured out a round-about solution.
First I convert the meters into unity world space using whatever IMapScalingStrategy Mapbox is currently using.
Then I convert from world to the view space of whatever camera I want to scale to the given bounds.
After that, I use find out the scale of the bounds, solving for:
UnityRelativeScaleChange = 2Map Zoom Level Change; which (to my estimations) is the relationship between unity scale and mapbox zoom levels.
This solutions works great as long as you don't have to zoom in/out by too much, otherwise you'll run into precision problems as the functions rely on the relative view-based size of a given bounds to do their calculations which will lead to unstable results if those initially take a tiny portion of the screen.

Creating a Dynamic Google Earth Web Link

I've been tasked with creating a Google Earth Web link programmatically when given coordinates. I have the street address as well, where I'd ideally like to drop a pin.
For example, I can get a link to the white house using its lat/lon at a distance of 150 meters like this:
https://earth.google.com/web/#38.8976633,-77.0365739,150d
If I search using the google earth web app I can generate a link with a pin, where a few of the parameters in the link change slightly:
https://earth.google.com/web/#38.8976763,-77.0365298,18.0497095a,800.41606338d,35y,0h,45t,0r/data=ChIaEAoIL20vMDgxc3EYAiABKAIoAg
Am I able to dynamically generate the data element, or whichever element creates the pin, at my desired location? I've also had trouble finding the correct distance d and elevation a parameters in my links.
As you found, you can generate links to specific views in the Google Earth web client by adding the correct parameters to the URL, including the latitude, longitude and altitude (a) of the view target, and the distance (d) of the camera from that target. Note that altitude and distance are both in meters, and altitude is above sea level, not above ground elevation. If you look at the a and d parameters that Earth puts in the URL as you fly around, often altitude will be the terrain (or builing-top) elevation at the target lat/lon, and the distance will be how far the camera is from that altitude. The other available parameters include heading (h) and roll (r).
So long as your tilt (t) remains zero, then altitude and distance should be interchangeable, or if both are >0, then they will be summed together for the final camera height above sea level. But if you add a tilt (zero degrees is looking straight down), then the altitude determines the elevation of the view target (above the lat & lon location), and the distance determines how far the camera is from that point. If you make d=0, then altitude will define both the view target and camera height above sea level. If you make a=0, then the distance will be from the lat,lon at sea level (even if that's underground).
Unfortunately there's no way to manually construct the data parameter, as it can contain many different things. To do that right would require an API, which Earth for Web currently does not provide. Hopefully that kind of functionality will come after Earth finishes its work to become cross-browser compatible via Web Assembly. Until then, there's not a way to add a point the map via just a URL.

Get Latitude and Longitude of upper left corner of a Bing Map

I programatically create requests to dev.virtualearth.net (Bing static maps).
I know the following values:
Center Point (Latitude & Longitude)
Zoom Level
Map Size (X pixels, Y pixels)
After I recieved the map as a bitmap, how do I determine the Coordinates (Latitude and Longitude) of the upper left corner (basically the very first pixel) and the lower right corner (the very last pixel)?
I just need some suggestions or some pseudo code. Note, that while I know the Center Point, Zoom Level and Map Size, these aren't the same for every request.
Thank you.
You will need to do tile math: https://msdn.microsoft.com/en-us/library/bb259689.aspx
You will need to do the following:
Pass the center point into LatLongToPixelXY method to get the center global pixel value.
Knowing the pixel dimensions of the static image you created, subtract half the width from the x value of the center global pixel value. Do the same with the height and y.
This gives you a new pixel value, pass it into the PixelXYToLatLong to get the coordinate for the top left corner.
That's it :)
I have an old code sample that does this, but retrieves the static image using the old SOAP services rather than the REST services. You can find the blog post here: https://rbrundritt.wordpress.com/2008/10/25/ve-imagery-service-and-custom-icons/ See the LatLongToPixel function code that is half way down the post. That does the above three steps.

Why points above 85N or below -85S are not shown on the iPhone map?

I need to represent on the map (iPhone) some points such as (88, 60) or (90, 55), but the custom annotations representing these points get deallocated. I also noticed that these points are not actually displayed on the google map, they are somehow above the visible map. This happens for any point that is above 85 deg. N latitude or -85 deg. S latitude.
I know it's a really old post, but just to answer your question.
Most (allmost all) commercial maps are displayed in the mercator projection. This is what you see in mapKit or on google maps. This means that the latitude and longitude lines run horizontal and vertical.
If you would change this to for instance polar project (world from the top) it would become way to difficult to calculate the postion of objects because the lat and long lines converge rapidly...
So it's just for ease of use....

MKMapView setRegion "snaps" to predefined zoom levels?

Can anyone confirm that setRegion "snaps" to predefined zoom levels and whether or not this behavior is as designed (although undocumented) or a known bug? Specifically, it appears that setRegion snaps to the same zoom levels that correspond to the zoom levels used when the user double-taps the map.
I'm trying to restore a previously saved region but this behavior makes it impossible if the saved region was set via a pinch zoom and not a double-tap zoom.
A big clue to me that things are broken on the mapkit side is what occurs if I call regionThatFits on the map's current region. It should return the same region (since it obviously fits the map's frame) but it returns the region that corresponds to the next higher predefined zoom level instead.
setVisibleMapRect behaves similarly.
Any further insight or information would be appreciated.
I found these related posts but neither included a solution or definitive confirmation that this is in fact a mapkit bug:
MKMapView setRegion: odd behavior?
MKMapView show incorrectly saved region
EDIT:
Here is an example that demonstrates the problem. All values are valid for my map view's aspect ratio:
MKCoordinateRegion initialRegion;
initialRegion.center.latitude = 47.700200f;
initialRegion.center.longitude = -122.367109f;
initialRegion.span.latitudeDelta = 0.065189f;
initialRegion.span.longitudeDelta = 0.067318f;
[map setRegion:initialRegion animated:NO];
NSLog(#"DEBUG initialRegion: %f %f %f %f", initialRegion.center.latitude, initialRegion.center.longitude, initialRegion.span.latitudeDelta, initialRegion.span.longitudeDelta);
NSLog(#"DEBUG map.region: %f %f %f %f", map.region.center.latitude, map.region.center.longitude, map.region.span.latitudeDelta, map.region.span.longitudeDelta);
OUTPUT:
DEBUG initialRegion: 47.700199 -122.367111 0.065189 0.067318
DEBUG map.region: 47.700289 -122.367096 0.106287 0.109863
Note the discrepancy in the latitude/longitude delta values. The map's values are almost double what I requested. The larger values correspond to one of the zoom levels used when the user double-taps the map.
Yes, it snaps to discrete levels. I've done quite a bit of experimentation, and it seems to like multiples of 2.68220906e-6 degrees of longitude per pixel.
So if your map fills the whole width of the screen, the first level spans .0008583 degrees, then the next level up you can get is twice that, .001717, and then the next one is twice that, .003433, and so on. I'm not sure why they chose to normalize by longitude, it means that fixes zoom levels vary depending on what part of the world you are looking at.
I've also spent a lot of time trying to understand the significance of that number .68220906e-6 degrees. It comes out to about 30cm at the equator, which kind of makes sense since the high resolution photos used by Google Maps have a 30cm resolution, but I would have expected them to use latitude instead of longitude to establish the zoom levels. That way, at maximum zoom, you always the native resolution of the satellite images, but who knows, they probably have some smart-people reason for making it work like that.
In my application I need to display a certain range of latitude. I'm gonna work on some code to try to zoom the map as close as possible to that. If anyone is interested, contact me.
I found a solution.
If the received snapped zoom level, is, lets say a factor of 1.2 bigger than the desired one:
use this algorithm to correct:
Asumption: you want to set the map view to exactly show "longitudinalMeters" from left to right
1) Calculate the correction scale:
Calculate the relation between longitudinal span you received, to that one you have got.
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(center, 0, longitudinalMeters);
MKCoordinateRegion regionFits = [mapView regionThatFits: region];
double correctionFactor = regionFits.span.longitudeDelta / region.span.longitudeDelta;
2) Create the transformation and apply it to the map
CGAffineTransform mapTransform = CGAffineTransformMakeScale(correctionScale, correctionScale);
CGAffineTransform pinTransform = CGAffineTransformInvert(mapTransform);
[mapView setTransform:mapTransform];
3) Apply the inverse transformation to the Map pins, to keep them at original size
[mapView setTransform:mapTransform];
for (id<MKAnnotation> annotation in self.mapView.annotations)
{
[[self.mapView viewForAnnotation:annotation] setTransform:pinTransform];
}
The weird behavior seems to be due to the fact that while one requests a particular region or view size, the actual API call to google is invoked with a center point and a zoom level. E.G.:
map.setCenter(new google.maps.LatLng(234.3453, 454.2345), 42);
Now it would be possible for Apple to request the appropriate zoom level and then adjust the sizing of the view to accommodate the actual region request, but it seems they fail to do so. I am drawing bus routes on a map, and one of my routes barely triggers a larger zoom level and thus scales too small (under-zooms) and looks ugly and smashed.
#pseudopeach, Please update me on the progress of your attempts to work around this issue. If one could detect the boundaries of a zoom level, the region request could then be deliberately underscaled to avoid the under-zoom. Since you are onto this I would be interested in seeing your code before I have to make an attempt at it myself.
There is an interesting category that the author of the blog Backspace Prolog has written to enable the direct manipulation of the Google Maps API by emulating their setCenter(centerPoint,ZoomLevel) call signature. You can find it here. I haven't spent the time yet, but the math can probably be reverse engineered to yield a means of calculating the zoom level for a given Region or MapRect. Depending on how far it is within the zoom level's range - i.e. how far it is over the threshold that triggers the lower zoom level - it could decide whether to go to the lower level or keep to higher one by under-requesting.
This is clearly a behavioral bug that needs to be fixed so that MKMapView can be used in a more refined manner.
This is an old question, but I recently investigated Google maps in detail, and can share some insight. I don't know whether this is also valid for the current Apple maps.
The reason that the resolution snaps to predefined zoomlevels is because the original maps fetched from Google's servers are drawn with those zoomlevels. The size of the features on those maps are drawn with a certain resolution in mind. For example, the width (in pixels) of a road on those maps is always the same. On higher resolution maps, more secundary roads are drawn, but their width is always the same. The resolution snaps to predefined levels to make sure those features are always depicted with the same size. That is, it is not a bug but a feature.
Those predefined resolutions vary with latitude because of the Mercator projection of the maps. Mercator projection is easy to work with because latitude lines are depicted straight and horizontal and longitude lines are straight and vertical. But with Mercator projection the top of the map has a slightly higher resolution than the bottom (on the Northern hemisphere). That has consequences for fitting maps together at the northern and sourthern edges.
That is, when you start on the equator and drive north, then the resolution of the Mercator maps you drive over will gradually increase. The longitude lines remain vertical, and therefore the longitude spans remains the same. But the resolution increases, and therefore the latitude span decreases. Still, on all those maps the roads have the same width in pixels, and texts are depicted in the same font size, etc.
Google uses a Mercator projection where the equator circumference is 256 pixels at zoomlevel 0. Each next zoomlevel doubles that amount. That is, at zoomlevel 1, the equator is 512 pixels long, at zoomlevel 2, the equator is 1024 pixels long, etc. The model for the earth they use is a FAI globe with a radius of exactly 6371 km, or circumference of 40030 km.
Therefore, resolution for zoomLevel 0 at the equator is 156.37 km/pixel, at zoomlevel 1 it is 78.19 km/pixel, etc. Those resolutions then vary with the cosinus of the latitude anywhere else on the earth.
MKCoordinateRegion region;
region.center.latitude = latitude;
region.center.longitude = longitude;
region.span.latitudeDelta = 5.0;
region.span.longitudeDelta = 5.0;
[mapView setRegion:region animated:YES];
I restore the region with no problem and with no variance as you describe. It is really impossible to tell what is specifically wrong in your case without some code to look at but here's what works for me:
Save both the center and span values somewhere. When you are restoring them specifically set both the center and span.
Restoring should look like this:
MKCoordinateRegion initialRegion;
initialRegion.center.latitude = Value you've stored
initialRegion.center.longitude = Value you've stored
initialRegion.span.latitudeDelta = Value you've stored
initialRegion.span.longitudeDelta = Value you've stored
[self.mapView setRegion:initialRegion animated:NO];
Also remember that this method is available in 4.0: `mapRectThatFits:edgePadding: MapRectThatFits helpfully adds a reasonable border to ensure that say a map annotation on the edge is not obscured and the the rect that you're attempting to display is fully visible. If you want to control the border use the call that gives you access to set edgePadding as well.
If you set up the MapView in InterfaceBuilder, make sure you don't do this:
_mapView = [[MKMapView alloc] init];
As soon as I removed this init line, my map view suddenly began responding properly to all the updates I sent it. I suspect that what happens is that if you do the alloc init, it's actually creating another view that's not being shown anywhere. The one you see on the screen is the one initialized by your nib. But if you alloc init a new one, then that's something somewhere else and it's not going to do anything.