iphone - que - mkmapview events
¿Cómo verificar si MKCoordinateRegion contiene CLLocationCoordinate2D sin usar MKMapView? (8)
Necesito verificar si la ubicación del usuario pertenece a la MKCoordinateRegion. Me sorprendió no encontrar una función simple para esto, algo como: CGRectContainsCGPoint (rect, punto) .
Encontré el siguiente fragmento de código:
CLLocationCoordinate2D topLeftCoordinate =
CLLocationCoordinate2DMake(region.center.latitude
+ (region.span.latitudeDelta/2.0),
region.center.longitude
- (region.span.longitudeDelta/2.0));
CLLocationCoordinate2D bottomRightCoordinate =
CLLocationCoordinate2DMake(region.center.latitude
- (region.span.latitudeDelta/2.0),
region.center.longitude
+ (region.span.longitudeDelta/2.0));
if (location.latitude < topLeftCoordinate.latitude || location.latitude > bottomRightCoordinate.latitude || location.longitude < bottomRightCoordinate.longitude || location.longitude > bottomRightCoordinate.longitude) {
// Coordinate fits into the region
}
Sin embargo, no estoy seguro de que sea preciso, ya que la documentación no especifica exactamente cómo se calcula el rectángulo de la región.
Debe haber una forma más sencilla de hacerlo. ¿He pasado por alto alguna función en la documentación del marco de MapKit?
Basado en la solución de Lukasz, pero en Swift, en caso de que alguien pueda usar Swift:
func isInRegion (region : MKCoordinateRegion, coordinate : CLLocationCoordinate2D) -> Bool {
let center = region.center;
let northWestCorner = CLLocationCoordinate2D(latitude: center.latitude - (region.span.latitudeDelta / 2.0), longitude: center.longitude - (region.span.longitudeDelta / 2.0))
let southEastCorner = CLLocationCoordinate2D(latitude: center.latitude + (region.span.latitudeDelta / 2.0), longitude: center.longitude + (region.span.longitudeDelta / 2.0))
return (
coordinate.latitude >= northWestCorner.latitude &&
coordinate.latitude <= southEastCorner.latitude &&
coordinate.longitude >= northWestCorner.longitude &&
coordinate.longitude <= southEastCorner.longitude
)
}
En caso de que haya alguien más confundido con las latitudes y los largos, aquí se prueba, la solución de trabajo:
MKCoordinateRegion region = self.mapView.region;
CLLocationCoordinate2D location = user.gpsposition.coordinate;
CLLocationCoordinate2D center = region.center;
CLLocationCoordinate2D northWestCorner, southEastCorner;
northWestCorner.latitude = center.latitude - (region.span.latitudeDelta / 2.0);
northWestCorner.longitude = center.longitude - (region.span.longitudeDelta / 2.0);
southEastCorner.latitude = center.latitude + (region.span.latitudeDelta / 2.0);
southEastCorner.longitude = center.longitude + (region.span.longitudeDelta / 2.0);
if (
location.latitude >= northWestCorner.latitude &&
location.latitude <= southEastCorner.latitude &&
location.longitude >= northWestCorner.longitude &&
location.longitude <= southEastCorner.longitude
)
{
// User location (location) in the region - OK :-)
NSLog(@"Center (%f, %f) span (%f, %f) user: (%f, %f)| IN!", region.center.latitude, region.center.longitude, region.span.latitudeDelta, region.span.longitudeDelta, location.latitude, location.longitude);
}else {
// User location (location) out of the region - NOT ok :-(
NSLog(@"Center (%f, %f) span (%f, %f) user: (%f, %f)| OUT!", region.center.latitude, region.center.longitude, region.span.latitudeDelta, region.span.longitudeDelta, location.latitude, location.longitude);
}
Estoy publicando esta respuesta ya que la solución aceptada no es válida en mi opinión. Esta respuesta tampoco es perfecta, pero maneja el caso cuando las coordenadas envuelven alrededor de los límites de 360 grados, lo cual es suficiente para ser adecuado en mi situación.
+ (BOOL)coordinate:(CLLocationCoordinate2D)coord inRegion:(MKCoordinateRegion)region
{
CLLocationCoordinate2D center = region.center;
MKCoordinateSpan span = region.span;
BOOL result = YES;
result &= cos((center.latitude - coord.latitude)*M_PI/180.0) > cos(span.latitudeDelta/2.0*M_PI/180.0);
result &= cos((center.longitude - coord.longitude)*M_PI/180.0) > cos(span.longitudeDelta/2.0*M_PI/180.0);
return result;
}
He usado este código para determinar si una coordenada está dentro de una región circular (una coordenada con un radio a su alrededor).
- (BOOL)location:(CLLocation *)location isNearCoordinate:(CLLocationCoordinate2D)coordinate withRadius:(CLLocationDistance)radius
{
CLCircularRegion *circularRegion = [[CLCircularRegion alloc] initWithCenter:location.coordinate radius:radius identifier:@"radiusCheck"];
return [circularRegion containsCoordinate:coordinate];
}
Las otras respuestas todas tienen fallas. La respuesta aceptada es un poco detallada y falla cerca de la línea de datos internacional. La respuesta del coseno es viable, pero falla para regiones muy pequeñas (porque el coseno delta es sinusoidal, que tiende a cero cerca de cero, lo que significa que para las diferencias angulares más pequeñas esperamos un cambio cero) Esta respuesta debería funcionar correctamente para todas las situaciones, y es más simple.
Rápido:
/* Standardises and angle to [-180 to 180] degrees */
class func standardAngle(var angle: CLLocationDegrees) -> CLLocationDegrees {
angle %= 360
return angle < -180 ? -360 - angle : angle > 180 ? 360 - 180 : angle
}
/* confirms that a region contains a location */
class func regionContains(region: MKCoordinateRegion, location: CLLocation) -> Bool {
let deltaLat = abs(standardAngle(region.center.latitude - location.coordinate.latitude))
let deltalong = abs(standardAngle(region.center.longitude - location.coordinate.longitude))
return region.span.latitudeDelta >= deltaLat && region.span.longitudeDelta >= deltalong
}
C objetivo:
/* Standardises and angle to [-180 to 180] degrees */
+ (CLLocationDegrees)standardAngle:(CLLocationDegrees)angle {
angle %= 360
return angle < -180 ? -360 - angle : angle > 180 ? 360 - 180 : angle
}
/* confirms that a region contains a location */
+ (BOOL)region:(MKCoordinateRegion*)region containsLocation:(CLLocation*)location {
CLLocationDegrees deltaLat = fabs(standardAngle(region.center.latitude - location.coordinate.latitude))
CLLocationDegrees deltalong = fabs(standardAngle(region.center.longitude - location.coordinate.longitude))
return region.span.latitudeDelta >= deltaLat && region.span.longitudeDelta >= deltalong
}
Sin embargo, este método falla para las regiones que incluyen cualquiera de los polos, pero luego el sistema de coordenadas falla en los polos. Para la mayoría de las aplicaciones, esta solución debería ser suficiente. (Nota, no probado en Objetivo C)
Owen Godfrey, el código objetivo-C no funciona, este es el buen código: falla en Objective-C, este es el buen código:
/* Standardises and angle to [-180 to 180] degrees */
- (CLLocationDegrees)standardAngle:(CLLocationDegrees)angle {
angle=fmod(angle,360);
return angle < -180 ? -360 - angle : angle > 180 ? 360 - 180 : angle;
}
-(BOOL)thisRegion:(MKCoordinateRegion)region containsLocation:(CLLocation *)location{
CLLocationDegrees deltaLat =fabs([self standardAngle:(region.center.latitude-location.coordinate.latitude)]);
CLLocationDegrees deltaLong =fabs([self standardAngle:(region.center.longitude-location.coordinate.longitude)]);
return region.span.latitudeDelta >= deltaLat && region.span.longitudeDelta >=deltaLong;
}
CLLocationDegrees deltalong = fabs(standardAngle(region.center.longitude - location.coordinate.longitude));
return region.span.latitudeDelta >= deltaLat && region.span.longitudeDelta >= deltalong;
}
¡Gracias!
Puede convertir su ubicación a un punto con MKMapPointForCoordinate
, luego usar MKMapRectContainsPoint
en el visibleMapRect
del visibleMapRect
. Esto está completamente fuera de mi cabeza. Déjame saber si funciona.
Tuve problema con los mismos cálculos. Me gusta la concepción propuesta por Owen Godfrey here , pero incluso Fernando here perdió el hecho de que la latitud se desvía de manera diferente a la longitud y tiene un rango diferente. Para aclarar mi propuesta, la publico con pruebas para que pueda verla usted mismo.
import XCTest
import MapKit
// MARK - The Solution
extension CLLocationDegrees {
enum WrapingDimension: Double {
case latitude = 180
case longitude = 360
}
/// Standardises and angle to [-180 to 180] or [-90 to 90] degrees
func wrapped(diemension: WrapingDimension) -> CLLocationDegrees {
let length = diemension.rawValue
let halfLenght = length/2.0
let angle = self.truncatingRemainder(dividingBy: length)
switch diemension {
case .longitude:
// return angle < -180.0 ? 360.0 + angle : angle > 180.0 ? -360.0 + angle : angle
return angle < -halfLenght ? length + angle : angle > halfLenght ? -length + angle : angle
case .latitude:
// return angle < -90.0 ? -180.0 - angle : angle > 90.0 ? 180.0 - angle : angle
return angle < -halfLenght ? -length - angle : angle > halfLenght ? length - angle : angle
}
}
}
extension MKCoordinateRegion {
/// confirms that a region contains a location
func contains(_ coordinate: CLLocationCoordinate2D) -> Bool {
let deltaLat = abs((self.center.latitude - coordinate.latitude).wrapped(diemension: .latitude))
let deltalong = abs((self.center.longitude - coordinate.longitude).wrapped(diemension: .longitude))
return self.span.latitudeDelta/2.0 >= deltaLat && self.span.longitudeDelta/2.0 >= deltalong
}
}
// MARK - Unit tests
class MKCoordinateRegionContaingTests: XCTestCase {
func testRegionContains() {
var region = MKCoordinateRegionMake(CLLocationCoordinate2DMake(0, 0), MKCoordinateSpan(latitudeDelta: 1, longitudeDelta: 1))
var coords = CLLocationCoordinate2DMake(0, 0)
XCTAssert(region.contains(coords))
coords = CLLocationCoordinate2DMake(0.5, 0.5)
XCTAssert(region.contains(coords))
coords = CLLocationCoordinate2DMake(-0.5, 0.5)
XCTAssert(region.contains(coords))
coords = CLLocationCoordinate2DMake(0.5, 0.5000001)
XCTAssert(!region.contains(coords)) // NOT Contains
coords = CLLocationCoordinate2DMake(0.5, -0.5000001)
XCTAssert(!region.contains(coords)) // NOT Contains
coords = CLLocationCoordinate2DMake(1, 1)
XCTAssert(!region.contains(coords)) // NOT Contains
region = MKCoordinateRegionMake(CLLocationCoordinate2DMake(0, 180), MKCoordinateSpan(latitudeDelta: 1, longitudeDelta: 1))
coords = CLLocationCoordinate2DMake(0, 180.5)
XCTAssert(region.contains(coords))
coords.longitude = 179.5
XCTAssert(region.contains(coords))
coords.longitude = 180.5000001
XCTAssert(!region.contains(coords)) // NOT Contains
coords.longitude = 179.5000001
XCTAssert(region.contains(coords))
coords.longitude = 179.4999999
XCTAssert(!region.contains(coords)) // NOT Contains
region = MKCoordinateRegionMake(CLLocationCoordinate2DMake(90, -180), MKCoordinateSpan(latitudeDelta: 1, longitudeDelta: 1))
coords = CLLocationCoordinate2DMake(90.5, -180.5)
XCTAssert(region.contains(coords))
coords = CLLocationCoordinate2DMake(89.5, -180.5)
XCTAssert(region.contains(coords))
coords = CLLocationCoordinate2DMake(90.50000001, -180.5)
XCTAssert(!region.contains(coords)) // NOT Contains
coords = CLLocationCoordinate2DMake(89.50000001, -180.5)
XCTAssert(region.contains(coords))
coords = CLLocationCoordinate2DMake(89.49999999, -180.5)
XCTAssert(!region.contains(coords)) // NOT Contains
}
func testStandardAngle() {
var angle = 180.5.wrapped(diemension: .longitude)
var required = -179.5
XCTAssert(self.areAngleEqual(angle, required))
angle = 360.5.wrapped(diemension: .longitude)
required = 0.5
XCTAssert(self.areAngleEqual(angle, required))
angle = 359.5.wrapped(diemension: .longitude)
required = -0.5
XCTAssert(self.areAngleEqual(angle, required))
angle = 179.5.wrapped(diemension: .longitude)
required = 179.5
XCTAssert(self.areAngleEqual(angle, required))
angle = 90.5.wrapped(diemension: .latitude)
required = 89.5
XCTAssert(self.areAngleEqual(angle, required))
angle = 90.5000001.wrapped(diemension: .latitude)
required = 89.4999999
XCTAssert(self.areAngleEqual(angle, required))
angle = -90.5.wrapped(diemension: .latitude)
required = -89.5
XCTAssert(self.areAngleEqual(angle, required))
angle = -90.5000001.wrapped(diemension: .latitude)
required = -89.4999999
XCTAssert(self.areAngleEqual(angle, required))
}
/// compare doubles with presition to 8 digits after the decimal point
func areAngleEqual(_ a:Double, _ b:Double) -> Bool {
let presition = 0.00000001
let equal = Int(a / presition) == Int(b / presition)
print(String(format:"%14.9f %@ %14.9f", a, equal ? "==" : "!=", b) )
return equal
}
}