Pixels for meter length in MKMapView - ios

I am trying to create a function that takes in a parameter of meters and returns a pixel width for an MKMapView.
How can I correctly determine from the current zoom of the map, how many pixels represent the correct amount of meters?
So an example is let's say I have a zone radius of 78 meters, and the current distance across the map is 6000 meters. How can I represent those 78 meters as a scaled proportional UIView on the map?
My plan was to first calculate how many meters across the map are currently being represented. So here is what I have for that. But I am getting stuck on trying to figure out how to convert the amount of meters into the correct pixel widths. I already have the correct zone radiuses as well.
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
MKMapRect currentRect = mapView.visibleMapRect;
MKMapPoint farEastPoint = MKMapPointMake(MKMapRectGetMinX(currentRect), MKMapRectGetMidY(currentRect));
MKMapPoint farWestPoint = MKMapPointMake(MKMapRectGetMaxX(currentRect), MKMapRectGetMidY(currentRect));
double currentDis =MKMetersBetweenMapPoints(farEastPoint, farWestPoint);
}

Related

Calculate coordinates of map square from map center

I want to get the coordinates of the corners of the rectangle. Or to find the coordinate of the north-westest most point, 50 km from the map centre.
Does anyone know how I can do that?
The point is when I move around the map, I want to always have a rectangle(the rectangle does not need to drew, I just need its coordinates for a backend request), with it's corners always at 50 km from the current centre of the map.
I'm thinking of using somehow the distance function from CLLocation, but in this case I have the distance, but not one of the coordinates.
50km = mapCenterLocation.distance(from: coordinatesUnknown)
Not really sure what do you mean, but maybe this can help
func getNewTargetCoordinate(position: CLLocationCoordinate2D, userBearing: Float, distance: Float)-> CLLocationCoordinate2D{
//haversine formula
//r is earth radius
let r = 6378140.0
let latitude1 = position.latitude * (Double.pi/180);
let longitude1 = position.longitude * (Double.pi/180);
//bearing for user heading in degree
let brng = Double(userBearing) * (Double.pi/180);
//calculating user new position based on user distance and bearing can be seen at haversine formula
var latitude2 = asin(sin(latitude1)*cos(Double(distance)/r) + cos(latitude1)*sin(Double(distance)/r)*cos(brng));
var longitude2 = longitude1 + atan2(sin(brng)*sin(Double(distance)/r)*cos(latitude1),cos(Double(distance)/r)-sin(latitude1)*sin(latitude2));
//converting latitude as degree
latitude2 = latitude2 * (180/Double.pi)
longitude2 = longitude2 * (180/Double.pi)
// return location of user
return CLLocationCoordinate2DMake(latitude2, longitude2)
}
This work for NE direction and distance in meters
for the north-west direction, I think you can just put 135 for the degree and 5000 for distance.
For the position, you need to put map center location.
edit:
For custom rectangle., you can first check for the diagonal degree
func getDiagonalDegree(x: Float, y:Float) -> Float{
return atan2(y,x)*(180/Double.pi)
}
So now you can get that returned diagonal degree to and put it in getNewTargetCoordinate. New bearing is 270+diagonalDegree.
Not sure if I understand you correctly, but I think this could help, or at least point you on some direction
CLLocationCoordinate2D northWest;
northWest = [mapView convertPoint:CGPointMake(0, 0) toCoordinateFromView:mapView];
With this you will get the coordinates for the top left corner of the map, I think you just need to adjust this to set a point 50 km of your center and get the coordinate with this same logic.

How do I create a buffered LineString with a width defined in meters when using GEOSwift?

I'm hoping to find a way using GEOSwift to take a series of user's LatLngs, construct a linestring and buffer it to always be 30 meters wide regardless of the user's location. I feel like there must be an easier way than the path I'm going down and any help would be appreciated.
Background:
From what I can tell the buffer functions width parameter is currently defined in decimal degrees as my coordinate system is EPSG 4326, which makes calculating the width in meters difficult. I can get a rough estimation of meters per decimal degree for both longitude or latitude with the Haversine formula.
The problem I have is the series of points can move both latitudinally and longitudinally. So the buffer width I need in these cases lies somewhere between ThirtyMetersInLatDegrees and ThirtyMetersInLngDegrees. And in this case the width to supply to the buffer function becomes a weird approximation/ average of the user's overall longitudinal and latitudinal movement throughout the linestring related to ThirtyMetersInLngDegrees and ThirtyMetersInLatDegrees.
i.e. assuming ThirtyMetersInLngDegrees is the max:
ThirtyMetersInLatDegrees <= bufferWidth <= ThirtyMetersInLngDegrees
How can I better accomplish this?
Here's how I'm calculating meters per decimal degree:
//Earth’s radius
let R=6378137.0
let deviceLatitude = 37.535997
let OneMeterInLatDegrees = 1/R * (180/Double.pi)
let OneMeterInLngDegrees = 1/(R*cos(Double.pi*deviceLatitude/180)) * (180/Double.pi)
let ThirtyMetersInLatDegrees = 30 * latDegreesPerMeter
let ThirtyMetersInLngDegrees = 30 * lngDegreesPerMeter

Request mapkit annotations to the server by position and zoom level

I have this question:
I have an API server to request points nearest a position (lat & long) and distance (km)
I would to call this API when the user make a pan on the map..so I would to calculate the distance parameter in function of zoom level..
How can I obtain this?
At this moment I have this MapKit delegate method:
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
MKZoomScale currentZoomScale = mapView.bounds.size.width / mapView.visibleMapRect.size.width;
NSLog(#"currentZoom:%f", currentZoomScale);
[self.dataReader sendServerRequestWithCoordinate:mapView.region.center andDistance:[self getDistanceByZoomLevel:currentZoomScale];
}
- (float) getDistanceByZoomLevel:(MKZoomScale) zoomLevel {
/// ?????? ////
}
You don't need to calculate the distance based on a "zoom level" or "zoom scale".
Map Kit and Core Location have methods and functions to calculate distance given coordinates or map points.
Assume you want to use the diagonal distance (from top-left to bottom-right) covered by the currently visible map.
The corner coordinates (CLLocationCoordinate2Ds) can be obtained from the map view's region property and then you can use the distanceFromLocation method to calculate the distance between the two coordinates.
The corner map points (MKMapPoints) can be obtained from the map view's visibleMapRect property and then you can use the MKMetersBetweenMapPoints function to get the distance between them.
Here's an example using the map points:
MKMapRect vmr = mapView.visibleMapRect;
//vmr.origin is the top-left corner MKMapPoint
MKMapPoint bottomRightMapPoint =
MKMapPointMake(vmr.origin.x + vmr.size.width,
vmr.origin.y + vmr.size.height);
CLLocationDistance distanceMeters =
MKMetersBetweenMapPoints(vmr.origin, bottomRightMapPoint);
Of course, if you want kilometers instead of meters, divide distanceMeters by 1000.0.
Also, if by "distance" you actually want the radius (distance from the center point), then also divide distanceMeters by 2.0.

MKMapView How-to convert from longitude latitude back to cm?

1) I am using MKMapView to display a custom Image (for example width=350 cm,height =230 cm) in a MKOverlayView.
2) The center of the Image is now at longitude=0 and latitude = 0 and covers the whole world
4) I place a MKPointAnnotation at longitude = 60.749995 and latitude =56.091651
Now I want convert this Point(Longitude,Latitude) back to x,y in cm.
So that I can create a JPG on the server with the annotation on top of the Image.
So how do I calculate the x,y values?
Thanx Craig
so something like:
CLLocationCoordinate2D coordinateOrigin = CLLocationCoordinate2DMake(90, -180);
CLLocationCoordinate2D coordinateMax = CLLocationCoordinate2DMake(-90, 180);
MKMapPoint maxMap=MKMapPointForCoordinate(coordinateMax);
MKMapPoint minMap=MKMapPointForCoordinate(coordinateOrigin);
double width = maxMap.x-minMap.x;
double height = maxMap.y-minMap.y;
MKMapPoint p = MKMapPointForCoordinate(wanted_coord);//wanted_coord is the one needed
double pixel_x=p.x/width;
double pixel_y=p.y/height;
1) You're not really dealing with cm, you're dealing with pixels. An image has a certain number of pixels in each direction, the physical measurement of cm depends on how big your screen/printer's pixels are.
2) to convert from lat long to pixels use MKMapPoints via the MKMapPointForCoordinate function. That will give you a x/y coordinate and you'll need to scale those values to fit your custom image, therefore you need to work out what MKMapPoints it covers. For example if your image covered the entire world you could find the minimum values for MKMapPoint by using MKCoordinateForMapPoint with (-180,-90) and the maximum values with (180,90). Now you'll have the max/min for MKMapPoint's x and y, you know the max/min for your image, so it's trivial to scale from one to the other.

Best practice for using lat/long within a UIView (not MKMapView)

Basically i have a list of POI's (name,lat,long) and i want to draw them on the UIView, relative to my current lat/long. I'm looking for some best practice for mapping these POI (lat/long) to a UIView.
I don't want to use MKMapView (no need for displaying map-data).
I was reading:
http://developer.apple.com/library/ios/#documentation/general/conceptual/Devpedia-CocoaApp/CoordinateSystem.html
But I'm clueless how i get from a CLLocation to a (x,y) on my UIView. I only want to draw those POI's around my current location. So, for example if my screen would represent a 20 by 30 KM region, how do i map my POI's to their corresponding (x,y) coordinates?
Thanks.
What you're doing is a little strange, but you can convert latitude/longitude to a CGPoint-like struct called an MKMapPoint. An MKMapPoint has an x and y value which correspond to points on a map. Imagine if you laid out a flat map of the world, and 0,0 was the top left. MKMapPoint is a point on that map using that coordinate system.
Use the function MKMapPointForCoordinate() to convert a CLLocationCoordinate2D to an MKMapPoint
MKMapPoint myMapPoint = MKMapPointForCoordinate(myLocationCoordinate);
When you get the list of points, you'll have to do something like finding the max and min x and y values, then fitting all the points into your view using those values, otherwise you'll end up with a load of very close points in one place in your view.
My guess is that, for a 20KM by 30KM region, you can consider the earth to be flat and there fore linearly extrapolate the coordinates. I am sure you can google and find out as to how much distance is a difference in 0.00001 in latitude and longitude.
So if you have 20Km to be represented on X axis, and your current location is 30.1234567 in latitude, and 0.0000001 is 1 km then you can put your coordinate in the center of the screen and 30.1234557 as the left most X coordinate and so on.
I am not trying to provide an answer here, but just trying to think out loud, because I wanted to do some thing similar as well and did it as an Internet based app (without display though), where given two coordinates, I had to find the distance between them.
There are many (many) different approaches to modelling the planet and translating 3D coordinates onto a 2D surface, and the errors introduced by the various methods vary depending on what part of the globe you are. This question seems to cover most of what you are after though:
Converting Longitude & Latitude to X Y on a map with Calibration points
I think its best way (correctly work for Mercator projection map):
extension UIView
{
func addLocation(coordinate: CLLocationCoordinate2D)
{
// max MKMapPoint values
let maxY = Double(267995781)
let maxX = Double(268435456)
let mapPoint = MKMapPointForCoordinate(coordinate)
let normalizatePointX = CGFloat(mapPoint.x / maxX)
let normalizatePointY = CGFloat(mapPoint.y / maxY)
let pointView = UIView(frame: CGRectMake(0, 0, 5, 5))
pointView.center = CGPointMake(normalizatePointX * frame.width, normalizatePointY * frame.height)
pointView.backgroundColor = UIColor.blueColor()
addSubview(pointView)
}
}
My simple project for adding coordinate on UIView: https://github.com/Glechik/MapCoordinateDrawer

Resources