// // ApexMapView.m // OpenStreetMap // // Created by Jack on 2019/1/18. // Copyright © 2019 Jack Template. All rights reserved. // #import "ApexMapView.h" #import "UIView+RAConstraint.h" //static NSString * const kTileSource = @"https://map.apexshipping.com/osm_tiles/{z}/{x}/{y}.png"; //static NSString * const kTileSource = @"https://map.apexshipping.com/osmmt/{z}/{x}/{y}.png"; static NSString * const kTileSource = @"https://map.apexshipping.com/osmhd/{z}/{x}/{y}.png"; static int const tileSize = 512; static NSString * const kOpenStreetMapURL = @"https://www.openstreetmap.org/copyright"; static NSString * const kCopyright = @"OpenStreetMap"; @interface ApexMapView () @property (nonatomic,strong) UIButton *legalBtn; @property (nonatomic,strong) MKTileOverlay *tileOverlay; @property (nonatomic,weak) id internalDelegate; @end @implementation ApexMapView #pragma mark - Initial - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { [self setupMapView]; } return self; } - (void)awakeFromNib { [super awakeFromNib]; [self setupMapView]; } - (void)setupMapView { self.rotateEnabled = NO; [self apex_setDelegate:self]; [self addOverlay:self.tileOverlay]; // if (@available(iOS 13.0, *)) { // MKMapCameraZoomRange* zoom_range =[[MKMapCameraZoomRange alloc] initWithMinCenterCoordinateDistance:500]; // // [[MKMapCameraZoomRange alloc] initWithMinCenterCoordinateDistance:-1 maxCenterCoordinateDistance:10]; // [self setCameraZoomRange:zoom_range]; // } else { // // Fallback on earlier versions // } } - (void)layoutSubviews { [super layoutSubviews]; NSArray *subViews = self.subviews; for (UIView *subView in subViews) { if ([subView isMemberOfClass:NSClassFromString(@"MKAttributionLabel")]) { [subView removeFromSuperview]; } else if ([subView isMemberOfClass:[UIImageView class]]) { [subView removeFromSuperview]; } } if (![subViews containsObject:self.legalBtn]) { [self setupLegalView]; } } - (void)setupLegalView { [self addSubview:self.legalBtn]; [self.legalBtn ra_applyConstraints:^(RAConstraintMaker *maker) { maker.left.ra_equalTo(self.left).ra_offset(5.0f); maker.bottom.ra_equalTo(self.bottom).ra_offset(-5.0f); }]; } #pragma mark - Override - (void)setDelegate:(id)delegate { if (delegate) { __weak typeof(delegate) weakDelegate = delegate; _internalDelegate = weakDelegate; } else { _internalDelegate = nil; } } - (void)apex_setDelegate:(id)delegate { [super setDelegate:delegate]; } #pragma mark - Getter //- (double) zoomLevel { // //// //// double d= 360 * self.frame.size.width /256/self.region.span.longitudeDelta; //// return log2(d); //// // MKCoordinateRegion region = self.region; // // double centerPixelX = [MKMapView longitudeToPixelSpaceX: region.center.longitude]; // double topLeftPixelX = [MKMapView longitudeToPixelSpaceX: region.center.longitude - region.span.longitudeDelta / 2]; // // double scaledMapWidth = (centerPixelX - topLeftPixelX) * 2; // CGSize mapSizeInPixels = self.bounds.size; // double zoomScale = scaledMapWidth / mapSizeInPixels.width; // double zoomExponent = log(zoomScale/(512/256)) / log(2); // double zoomLevel = 20 - zoomExponent; // // return zoomLevel; //} - (MKTileOverlay *)tileOverlay { if (!_tileOverlay) { _tileOverlay = [[MKTileOverlay alloc] initWithURLTemplate:kTileSource]; _tileOverlay.canReplaceMapContent = YES; _tileOverlay.minimumZ = 0; _tileOverlay.maximumZ = 18; _tileOverlay.tileSize = CGSizeMake(tileSize, tileSize); } return _tileOverlay; } - (UIButton *)legalBtn { if (!_legalBtn) { _legalBtn = [UIButton buttonWithType:UIButtonTypeSystem]; _legalBtn.titleLabel.font = [UIFont systemFontOfSize:12.0f]; [_legalBtn setTitle:kCopyright forState:UIControlStateNormal]; [_legalBtn addTarget:self action:@selector(legalButtonDidClick:) forControlEvents:UIControlEventTouchUpInside]; } return _legalBtn; } #pragma mark - User Action - (void)legalButtonDidClick:(id)sender { NSURL *url = [NSURL URLWithString:kOpenStreetMapURL]; if ([[UIApplication sharedApplication] canOpenURL:url]) { if (@available(iOS 10, *)) { [[UIApplication sharedApplication] openURL:url options:@{UIApplicationOpenURLOptionsSourceApplicationKey: @YES} completionHandler:^(BOOL success) { }]; } else{ [[UIApplication sharedApplication] openURL:url]; } } } #pragma mark - MKMapViewDelegate - (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:regionWillChangeAnimated:)]) { [_internalDelegate mapView:mapView regionWillChangeAnimated:animated]; } } - (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated { double z=[self zoomLevel]; double pi =3.1415926; int x= floor((mapView.centerCoordinate.longitude + 180) / 360 * pow(2 , z)); int y= floor((1 - log(tan(mapView.centerCoordinate.latitude * pi / 180) + 1 / cos(mapView.centerCoordinate.latitude * pi / 180)) / pi) / 2 * pow(2 , z)); NSLog(@"地图控件完成了改变 x:%d,y:%d,z:%f",x,y,z); if(mapView.zoomLevel>18) { NSLog(@"地图控件完成了改变 zoom to 18"); [mapView setCenterCoordinate:mapView.centerCoordinate zoomLevel:18 animated:true]; } if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:regionDidChangeAnimated:)]) { [_internalDelegate mapView:mapView regionDidChangeAnimated:animated]; } } - (void)mapViewDidChangeVisibleRegion:(MKMapView *)mapView { // double z=[self zoomLevel]; // // double pi =3.1415926; // int x= floor((mapView.centerCoordinate.longitude + 180) / 360 * pow(2 , z)); // // int y= floor((1 - log(tan(mapView.centerCoordinate.latitude * pi / 180) + 1 / cos(mapView.centerCoordinate.latitude * pi / 180)) / pi) / 2 * pow(2 , z)); // // NSLog(@"地图控件完成了改变 x:%d,y:%d,z:%f",x,y,z); // // if((int)z>18) // { // // // [mapView setCenterCoordinate:mapView.centerCoordinate zoomLevel:18 animated:true]; // // } if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapViewDidChangeVisibleRegion:)]) { [_internalDelegate mapViewDidChangeVisibleRegion:mapView]; } } - (void)mapViewWillStartLoadingMap:(MKMapView *)mapView { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapViewWillStartLoadingMap:)]) { [_internalDelegate mapViewWillStartLoadingMap:mapView]; } } - (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapViewDidFinishLoadingMap:)]) { [_internalDelegate mapViewDidFinishLoadingMap:mapView]; } } - (void)mapViewDidFailLoadingMap:(MKMapView *)mapView withError:(NSError *)error { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapViewDidFailLoadingMap:withError:)]) { [_internalDelegate mapViewDidFailLoadingMap:mapView withError:error]; } } - (void)mapViewWillStartRenderingMap:(MKMapView *)mapView { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapViewWillStartRenderingMap:)]) { [_internalDelegate mapViewWillStartRenderingMap:mapView]; } } - (void)mapViewDidFinishRenderingMap:(MKMapView *)mapView fullyRendered:(BOOL)fullyRendered { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapViewDidFinishRenderingMap:fullyRendered:)]) { [_internalDelegate mapViewDidFinishRenderingMap:mapView fullyRendered:fullyRendered]; } } // mapView:viewForAnnotation: provides the view for each annotation. // This method may be called for all or some of the added annotations. // For MapKit provided annotations (eg. MKUserLocation) return nil to use the MapKit provided annotation view. - (nullable MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id )annotation { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:viewForAnnotation:)]) { return [_internalDelegate mapView:mapView viewForAnnotation:annotation]; } return nil; } // mapView:didAddAnnotationViews: is called after the annotation views have been added and positioned in the map. // The delegate can implement this method to animate the adding of the annotations views. // Use the current positions of the annotation views as the destinations of the animation. - (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:didAddAnnotationViews:)]) { [_internalDelegate mapView:mapView didAddAnnotationViews:views]; } } #if TARGET_OS_IPHONE // mapView:annotationView:calloutAccessoryControlTapped: is called when the user taps on left & right callout accessory UIControls. - (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:annotationView:calloutAccessoryControlTapped:)]) { [_internalDelegate mapView:mapView annotationView:view calloutAccessoryControlTapped:control]; } } #endif - (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:didSelectAnnotationView:)]) { [_internalDelegate mapView:mapView didSelectAnnotationView:view]; } } - (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:didDeselectAnnotationView:)]) { [_internalDelegate mapView:mapView didDeselectAnnotationView:view]; } } - (void)mapViewWillStartLocatingUser:(MKMapView *)mapView { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapViewWillStartLocatingUser:)]) { [_internalDelegate mapViewWillStartLocatingUser:mapView]; } } - (void)mapViewDidStopLocatingUser:(MKMapView *)mapView { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapViewDidStopLocatingUser:)]) { [_internalDelegate mapViewDidStopLocatingUser:mapView]; } } - (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:didUpdateUserLocation:)]) { [_internalDelegate mapView:mapView didUpdateUserLocation:userLocation]; } } - (void)mapView:(MKMapView *)mapView didFailToLocateUserWithError:(NSError *)error { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:didFailToLocateUserWithError:)]) { [_internalDelegate mapView:mapView didFailToLocateUserWithError:error]; } } - (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view didChangeDragState:(MKAnnotationViewDragState)newState fromOldState:(MKAnnotationViewDragState)oldState { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:annotationView:didChangeDragState:fromOldState:)]) { [_internalDelegate mapView:mapView annotationView:view didChangeDragState:newState fromOldState:oldState]; } } #if TARGET_OS_IPHONE - (void)mapView:(MKMapView *)mapView didChangeUserTrackingMode:(MKUserTrackingMode)mode animated:(BOOL)animated { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:didChangeUserTrackingMode:animated:)]) { [_internalDelegate mapView:mapView didChangeUserTrackingMode:mode animated:animated]; } } #endif - (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id )overlay { if (overlay == self.tileOverlay) { return [[MKTileOverlayRenderer alloc] initWithTileOverlay:overlay]; } else { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:rendererForOverlay:)]) { return [_internalDelegate mapView:mapView rendererForOverlay:overlay]; } else { return nil; } } } - (void)mapView:(MKMapView *)mapView didAddOverlayRenderers:(NSArray *)renderers { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:didAddOverlayRenderers:)]) { [_internalDelegate mapView:mapView didAddOverlayRenderers:renderers]; } } #if TARGET_OS_IPHONE // Prefer -mapView:rendererForOverlay: - (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id )overlay { if (overlay == self.tileOverlay) { return [[MKOverlayView alloc] initWithOverlay:overlay]; } else { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:viewForOverlay:)]) { return [_internalDelegate mapView:mapView viewForOverlay:overlay]; } else { return nil; } } } // Called after the provided overlay views have been added and positioned in the map. // Prefer -mapView:didAddOverlayRenderers: - (void)mapView:(MKMapView *)mapView didAddOverlayViews:(NSArray *)overlayViews { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:didAddOverlayViews:)]) { [_internalDelegate mapView:mapView didAddOverlayViews:overlayViews]; } } #endif // Return nil for default MKClusterAnnotation, it is illegal to return a cluster annotation not containing the identical array of member annotations given. - (MKClusterAnnotation *)mapView:(MKMapView *)mapView clusterAnnotationForMemberAnnotations:(NSArray>*)memberAnnotations { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:clusterAnnotationForMemberAnnotations:)]) { return [_internalDelegate mapView:mapView clusterAnnotationForMemberAnnotations:memberAnnotations]; } return nil; } @end