#import "TGLocationUtils.h" #import "LegacyComponentsInternal.h" #import "TGLocalization.h" #import @implementation TGLocationUtils + (MKMapRect)MKMapRectForCoordinateRegion:(MKCoordinateRegion)region { MKMapPoint a = MKMapPointForCoordinate(CLLocationCoordinate2DMake(region.center.latitude + region.span.latitudeDelta / 2, region.center.longitude - region.span.longitudeDelta / 2)); MKMapPoint b = MKMapPointForCoordinate(CLLocationCoordinate2DMake(region.center.latitude - region.span.latitudeDelta / 2, region.center.longitude + region.span.longitudeDelta / 2)); return MKMapRectMake(MIN(a.x,b.x), MIN(a.y,b.y), ABS(a.x-b.x), ABS(a.y-b.y)); } + (bool)requestWhenInUserLocationAuthorizationWithLocationManager:(CLLocationManager *)locationManager { CLAuthorizationStatus authorizationStatus = [CLLocationManager authorizationStatus]; if (authorizationStatus == kCLAuthorizationStatusDenied || authorizationStatus == kCLAuthorizationStatusRestricted) return false; if ([locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) { if (authorizationStatus == kCLAuthorizationStatusNotDetermined) [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"TG_askedForAlwaysAuthorization_v0"]; [locationManager requestWhenInUseAuthorization]; return true; } return false; } + (bool)requestAlwaysUserLocationAuthorizationWithLocationManager:(CLLocationManager *)locationManager { CLAuthorizationStatus authorizationStatus = [CLLocationManager authorizationStatus]; if (authorizationStatus == kCLAuthorizationStatusDenied || authorizationStatus == kCLAuthorizationStatusRestricted) return false; if ([locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) { NSString *key = @"TG_askedForAlwaysAuthorization_v0"; bool askedForAlwaysAuthorization = [[[NSUserDefaults standardUserDefaults] objectForKey:key] boolValue]; if (authorizationStatus == kCLAuthorizationStatusAuthorizedWhenInUse && askedForAlwaysAuthorization) return false; [locationManager requestAlwaysAuthorization]; [[NSUserDefaults standardUserDefaults] setObject:@true forKey:key]; return true; } return false; } + (NSString *)stringFromDistance:(CLLocationDistance)distance { if (iosMajorVersion() >= 7) { MKDistanceFormatter *formatter = [self sharedDistanceFormatter]; NSString *systemLocale = [NSLocale currentLocale].localeIdentifier; NSString *finalLocale = legacyEffectiveLocalization().code; NSRange range = [systemLocale rangeOfString:@"_"]; if (range.location != NSNotFound) { finalLocale = [finalLocale stringByAppendingString:[systemLocale substringFromIndex:range.location]]; } formatter.locale = [NSLocale localeWithLocaleIdentifier:finalLocale]; if ([[formatter.locale objectForKey:NSLocaleUsesMetricSystem] boolValue]) formatter.unitStyle = MKDistanceFormatterUnitStyleAbbreviated; else formatter.unitStyle = MKDistanceFormatterUnitStyleDefault; return [[self sharedDistanceFormatter] stringFromDistance:distance]; } else { return [self _customStringFromDistance:distance]; } } + (NSString *)stringFromAccuracy:(CLLocationAccuracy)accuracy { if (iosMajorVersion() >= 7) { MKDistanceFormatter *formatter = [self sharedAccuracyFormatter]; //formatter.locale = [NSLocale localeWithLocaleIdentifier:legacyEffectiveLocalization().code]; return [[self sharedAccuracyFormatter] stringFromDistance:accuracy]; } else { return [self _customStringFromDistance:accuracy]; } } + (NSString *)stringForCoordinate:(CLLocationCoordinate2D)coordinate { NSInteger latSeconds = (NSInteger)(coordinate.latitude * 3600); NSInteger latDegrees = latSeconds / 3600; latSeconds = labs(latSeconds % 3600); NSInteger latMinutes = latSeconds / 60; latSeconds %= 60; NSInteger longSeconds = (NSInteger)(coordinate.longitude * 3600); NSInteger longDegrees = longSeconds / 3600; longSeconds = labs(longSeconds % 3600); NSInteger longMinutes = longSeconds / 60; longSeconds %= 60; NSString *result = [NSString stringWithFormat:@"%@%02ld° %02ld' %02ld\" %@%02ld° %02ld' %02ld\"", latDegrees >= 0 ? @"N" : @"S", labs(latDegrees), (long)latMinutes, (long)latSeconds, longDegrees >= 0 ? @"E" : @"W", labs(longDegrees), (long)longMinutes, (long)longSeconds]; return result; } + (NSString *)_customStringFromDistance:(CLLocationDistance)distance { NSLocale *locale = [NSLocale localeWithLocaleIdentifier:legacyEffectiveLocalization().code]; bool metricUnits = [[locale objectForKey:NSLocaleUsesMetricSystem] boolValue]; NSString *distanceString = nil; if (metricUnits) { if (distance >= 1000 * 1000) distanceString = [[NSString alloc] initWithFormat:@"%.1fK km", distance / (1000.0 * 1000.0)]; else if (distance > 1000) distanceString = [[NSString alloc] initWithFormat:@"%.1f km", distance / 1000.0]; else distanceString = [[NSString alloc] initWithFormat:@"%d m", (int)distance]; } else { double feetDistance = distance / 0.3048; if (feetDistance >= 5280) { char buf[32]; snprintf(buf, 32, "%.1f", feetDistance / 5280.0); bool dot = false; for (int i = 0; i < 32; i++) { char c = buf[i]; if (c == '\0') break; else if (c < '0' || c > '9') { dot = true; break; } } distanceString = [[NSString alloc] initWithFormat:@"%s mile%s", buf, dot || feetDistance / 5280.0 > 1.0 ? "s" : ""]; } else { distanceString = [[NSString alloc] initWithFormat:@"%d %s", (int)feetDistance, (int)feetDistance != 1 ? "feet" : "foot"]; } } return distanceString; } + (MKDistanceFormatter *)sharedDistanceFormatter { static dispatch_once_t once; static MKDistanceFormatter *distanceFormatter; dispatch_once(&once, ^ { distanceFormatter = [[MKDistanceFormatter alloc] init]; }); return distanceFormatter; } + (MKDistanceFormatter *)sharedAccuracyFormatter { static dispatch_once_t once; static MKDistanceFormatter *accuracyFormatter; dispatch_once(&once, ^ { accuracyFormatter = [[MKDistanceFormatter alloc] init]; accuracyFormatter.unitStyle = MKDistanceFormatterUnitStyleFull; }); return accuracyFormatter; } @end const NSInteger TGGoogleMapsOffset = 268435456; const CGFloat TGGoogleMapsRadius = TGGoogleMapsOffset / (CGFloat)M_PI; @implementation TGLocationUtils (GoogleMaps) + (CLLocationCoordinate2D)adjustGMapCoordinate:(CLLocationCoordinate2D)coordinate withPixelOffset:(CGPoint)offset zoom:(NSInteger)zoom { return CLLocationCoordinate2DMake([self adjustGMapLatitude:coordinate.latitude withPixelOffset:(NSInteger)offset.y zoom:zoom], [self adjustGMapLongitude:coordinate.longitude withPixelOffset:(NSInteger)offset.x zoom:zoom]); } + (CLLocationDegrees)adjustGMapLatitude:(CLLocationDegrees)latitude withPixelOffset:(NSInteger)offset zoom:(NSInteger)zoom { return [self _yToLatitude:([self _latitudeToY:latitude] + (offset << (21 - zoom)))]; } + (CLLocationDegrees)adjustGMapLongitude:(CLLocationDegrees)longitude withPixelOffset:(NSInteger)offset zoom:(NSInteger)zoom { return [self _xToLongitude:([self _longitudeToX:longitude] + (offset << (21 - zoom)))]; } + (NSInteger)_latitudeToY:(CLLocationDegrees)latitude { return (NSInteger)round(TGGoogleMapsOffset - TGGoogleMapsRadius * log((1 + sin(latitude * M_PI / 180.0)) / (1 - sin(latitude * M_PI / 180.0))) / 2); } + (CLLocationDegrees)_yToLatitude:(NSInteger)y { return (M_PI_2 - 2 * atan(exp((y - TGGoogleMapsOffset) / TGGoogleMapsRadius))) * 180.0 / M_PI; } + (NSInteger)_longitudeToX:(CLLocationDegrees)longitude { return (NSInteger)round(TGGoogleMapsOffset + TGGoogleMapsRadius * longitude * M_PI / 180); } + (CLLocationDegrees)_xToLongitude:(NSInteger)x { return (x - TGGoogleMapsOffset) / TGGoogleMapsRadius * 180.0 / M_PI; } @end @implementation TGLocationUtils (ThirdPartyAppLauncher) #pragma mark Apple Maps + (void)openMapsWithCoordinate:(CLLocationCoordinate2D)coordinate withDirections:(bool)withDirections locationName:(NSString *)locationName { MKPlacemark *placemark = [[MKPlacemark alloc] initWithCoordinate:coordinate addressDictionary:nil]; MKMapItem *mapItem = [[MKMapItem alloc] initWithPlacemark:placemark]; [mapItem setName:locationName]; if (withDirections) { NSDictionary *options = @{ MKLaunchOptionsDirectionsModeKey : MKLaunchOptionsDirectionsModeDriving }; MKMapItem *currentLocationMapItem = [MKMapItem mapItemForCurrentLocation]; [MKMapItem openMapsWithItems:@[ currentLocationMapItem, mapItem ] launchOptions:options]; } else { [mapItem openInMapsWithLaunchOptions:nil]; } } #pragma mark Google Maps + (void)openGoogleMapsWithCoordinate:(CLLocationCoordinate2D)coordinate withDirections:(bool)withDirections { NSURL *url = nil; NSString *coordinatePair = [NSString stringWithFormat:@"%f,%f", coordinate.latitude, coordinate.longitude]; if (withDirections) { url = [NSURL URLWithString:[[NSString alloc] initWithFormat:@"comgooglemaps-x-callback://?daddr=%@&directionsmode=driving&x-success=telegram://?resume=true&&x-source=Telegram", coordinatePair]]; } else { url = [NSURL URLWithString:[[NSString alloc] initWithFormat:@"comgooglemaps-x-callback://?center=%@&q=%@&x-success=telegram://?resume=true&&x-source=Telegram", coordinatePair, coordinatePair]]; } [[LegacyComponentsGlobals provider] openURL:url]; } + (bool)isGoogleMapsInstalled { return [[LegacyComponentsGlobals provider] canOpenURL:[NSURL URLWithString:@"comgooglemaps-x-callback://"]]; } + (void)openGoogleWithPlaceId:(NSString *)placeId { NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://maps.google.com/maps/place?cid=%@", placeId]]; [[LegacyComponentsGlobals provider] openURL:url]; } #pragma mark Foursquare + (void)openFoursquareWithVenueId:(NSString *)venueId { NSURL *url = nil; if ([self isFoursquareInstalled]) url = [NSURL URLWithString:[NSString stringWithFormat:@"foursquare://venues/%@", venueId]]; else url = [NSURL URLWithString:[NSString stringWithFormat:@"https://foursquare.com/venue/%@", venueId]]; [[LegacyComponentsGlobals provider] openURL:url]; } + (bool)isFoursquareInstalled { return [[LegacyComponentsGlobals provider] canOpenURL:[NSURL URLWithString:@"foursquare://"]]; } #pragma mark Here Maps + (void)openHereMapsWithCoordinate:(CLLocationCoordinate2D)coordinate { NSURL *url = [NSURL URLWithString:[[NSString alloc] initWithFormat:@"here-location://%f,%f", coordinate.latitude, coordinate.longitude]]; [[LegacyComponentsGlobals provider] openURL:url]; } + (bool)isHereMapsInstalled { return [[LegacyComponentsGlobals provider] canOpenURL:[NSURL URLWithString:@"here-location://"]]; } #pragma mark Yandex Maps + (void)openYandexMapsWithCoordinate:(CLLocationCoordinate2D)coordinate withDirections:(bool)withDirections { NSURL *url = nil; if (withDirections) { url = [NSURL URLWithString:[[NSString alloc] initWithFormat:@"yandexmaps://build_route_on_map?lat_to=%f&lon_to=%f", coordinate.latitude, coordinate.longitude]]; } else { url = [NSURL URLWithString:[[NSString alloc] initWithFormat:@"yandexmaps://maps.yandex.ru/?pt=%f,%f&z=16", coordinate.longitude, coordinate.latitude]]; } [[LegacyComponentsGlobals provider] openURL:url]; } + (bool)isYandexMapsInstalled { return [[LegacyComponentsGlobals provider] canOpenURL:[NSURL URLWithString:@"yandexmaps://"]]; } #pragma mark Yandex Navigator + (void)openDirectionsInYandexNavigatorWithCoordinate:(CLLocationCoordinate2D)coordinate { NSURL *url = [NSURL URLWithString:[[NSString alloc] initWithFormat:@"yandexnavi://build_route_on_map?lat_to=%f&lon_to=%f", coordinate.latitude, coordinate.longitude]]; [[LegacyComponentsGlobals provider] openURL:url]; } + (bool)isYandexNavigatorInstalled { return [[LegacyComponentsGlobals provider] canOpenURL:[NSURL URLWithString:@"yandexnavi://"]]; } #pragma mark - Waze + (void)openWazeWithCoordinate:(CLLocationCoordinate2D)coordinate withDirections:(bool)withDirections { NSURL *url = nil; if (withDirections) { url = [NSURL URLWithString:[[NSString alloc] initWithFormat:@"waze://?ll=%f,%f&navigate=yes", coordinate.latitude, coordinate.longitude]]; } else { url = [NSURL URLWithString:[[NSString alloc] initWithFormat:@"waze://?ll=%f,%f", coordinate.latitude, coordinate.longitude]]; } [[LegacyComponentsGlobals provider] openURL:url]; } + (bool)isWazeInstalled { return [[LegacyComponentsGlobals provider] canOpenURL:[NSURL URLWithString:@"waze://"]]; } @end