From 4c2aec2355b166174bd062b1d27f8f89fad6008c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Tue, 6 Dec 2016 23:36:55 -0800 Subject: [ios, macos] More ways to reshape an MGLMultiPoint (#7251) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [ios, macos] Completed API for mutating multipoints Added the complete set of methods for mutating the vertices of an MGLMultiPoint. Also rewrote MGLMultiPoint documentation to refer to vertices instead of points. * [ios, macos] Removed inaccurate MGLOverlay commentary This paragraph is full of references to features that exist in MKOverlay but not MGLOverlay. * [ios, macos] Lazily compute multipoint bounds Invalidate the bounds whenever the coordinates change, but don’t recompute the bounds until they’re requested. Simplified -intersectsOverlayBounds: for immutable overlay classes. Added a utility function for testing whether two MGLCoordinateBounds intersect, based on mbgl::LatLngBounds::intersects(). Removed unused color conversion code. --- platform/darwin/src/MGLGeometry.h | 8 ++ platform/darwin/src/MGLMultiPoint.h | 130 ++++++++++++++++++++++-------- platform/darwin/src/MGLMultiPoint.mm | 97 ++++++++++++++-------- platform/darwin/src/MGLOverlay.h | 8 -- platform/darwin/src/MGLPointCollection.mm | 13 ++- platform/darwin/src/MGLPolygon.mm | 2 +- platform/darwin/src/MGLPolyline.mm | 2 +- 7 files changed, 177 insertions(+), 83 deletions(-) (limited to 'platform/darwin') diff --git a/platform/darwin/src/MGLGeometry.h b/platform/darwin/src/MGLGeometry.h index e2a4d818b9..8e36b86d96 100644 --- a/platform/darwin/src/MGLGeometry.h +++ b/platform/darwin/src/MGLGeometry.h @@ -62,6 +62,14 @@ NS_INLINE BOOL MGLCoordinateBoundsEqualToCoordinateBounds(MGLCoordinateBounds bo bounds1.ne.longitude == bounds2.ne.longitude); } +/** Returns `YES` if the two coordinate bounds intersect. */ +NS_INLINE BOOL MGLCoordinateBoundsIntersectsCoordinateBounds(MGLCoordinateBounds bounds1, MGLCoordinateBounds bounds2) { + return (bounds1.ne.latitude > bounds2.sw.latitude && + bounds1.sw.latitude < bounds2.ne.latitude && + bounds1.ne.longitude > bounds2.sw.longitude && + bounds1.sw.longitude < bounds2.ne.longitude); +} + /** Returns `YES` if the coordinate is within the coordinate bounds. */ NS_INLINE BOOL MGLCoordinateInCoordinateBounds(CLLocationCoordinate2D coordinate, MGLCoordinateBounds bounds) { return (coordinate.latitude >= bounds.sw.latitude && diff --git a/platform/darwin/src/MGLMultiPoint.h b/platform/darwin/src/MGLMultiPoint.h index 3431fc2483..ed40ee9cad 100644 --- a/platform/darwin/src/MGLMultiPoint.h +++ b/platform/darwin/src/MGLMultiPoint.h @@ -7,67 +7,133 @@ NS_ASSUME_NONNULL_BEGIN /** The `MGLMultiPoint` class is an abstract superclass used to define shapes - composed of multiple points. You should not create instances of this class + composed of multiple vertices. You should not create instances of this class directly. Instead, you should create instances of the `MGLPolyline` or `MGLPolygon` classes. However, you can use the method and properties of this - class to access information about the specific points associated with the line - or polygon. + class to access information about the vertices of the line or polygon. */ @interface MGLMultiPoint : MGLShape /** - The array of coordinates associated with the shape. + The array of vertices associated with the shape. - This C array is a pointer to a structure inside the multipoint object, - which may have a lifetime shorter than the multipoint object and will - certainly not have a longer lifetime. Therefore, you should copy the C - array if it needs to be stored outside of the memory context in which you - use this property. + This C array is a pointer to a structure inside the multipoint object, which + may have a lifetime shorter than the multipoint object and will certainly not + have a longer lifetime. Therefore, you should copy the C array if it needs to + be stored outside of the memory context in which you use this property. */ @property (nonatomic, readonly) CLLocationCoordinate2D *coordinates NS_RETURNS_INNER_POINTER; -/** The number of coordinates associated with the shape. (read-only) */ +/** The number of vertices in the shape. */ @property (nonatomic, readonly) NSUInteger pointCount; /** - Retrieves one or more coordinates associated with the shape. + Retrieves the vertices of part of the shape. - @param coords On input, you must provide a C array of structures large enough - to hold the desired number of coordinates. On output, this structure - contains the requested coordinate data. - @param range The range of points you want. The `location` field indicates the - first point you are requesting, with `0` being the first point, `1` being - the second point, and so on. The `length` field indicates the number of - points you want. The array in _`coords`_ must be large enough to accommodate - the number of requested coordinates. + @param coords On input, you must provide a C array of `CLLocationCoordinate2D` + structures large enough to hold the desired number of coordinates. On + output, this structure contains the requested coordinate data. + @param range The range of vertices you want. The `location` field indicates + the first vertex you are requesting, with `0` being the first vertex, `1` + being the second vertex, and so on. The `length` field indicates the number + of vertices you want. The array in `coords` must be large enough to + accommodate the number of requested coordinates. */ - (void)getCoordinates:(CLLocationCoordinate2D *)coords range:(NSRange)range; /** - Updates one or more coordinates for the shape, which will instantaneously - cause the shape to be redrawn if it is currently visible on the map. + Sets the shape’s vertices to the given C array of vertices. - @param range The range of points to update. The `location` field indicates the - first point you are replacing, with `0` being the first point, `1` being - the second point, and so on. The `length` field indicates the number of - points to update. The array in _`coords`_ must be equal in number to the - length of the range. If you want to append to the existing coordinates - array use `-[MGLMultiPoint appendCoordinates:count:]`. @param coords The array of coordinates defining the shape. The data in this - array is copied to the object. + array is copied to the shape’s `coordinates` property. + @param count The number of coordinates from the `coords` array. */ -- (void)replaceCoordinatesInRange:(NSRange)range withCoordinates:(const CLLocationCoordinate2D *)coords; +- (void)setCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count; + +/** + Inserts the given vertices into the shape. If the shape is currently visible on + the map, it is redrawn immediately. + + @param coords The array of coordinates to insert into the shape. The data in + this array is copied to the shape’s `coordinate` property. + @param count The number of items in the `coords` array. + @param index The zero-based index at which the first coordinate in `coords` + will appear in the `coordinates` property. + */ +- (void)insertCoordinates:(const CLLocationCoordinate2D *)coords count:(NSUInteger)count atIndex:(NSUInteger)index; /** - Appends one or more coordinates for the shape, which will instantaneously - cause the shape to be redrawn if it is currently visible on the map. + Appends the given vertices to the shape. If the shape is currently visible on + the map, it is redrawn immediately. @param coords The array of coordinates to add to the shape. The data in this - array is copied to the new object. + array is copied to the shape’s `coordinate` property. @param count The number of items in the `coords` array. */ - (void)appendCoordinates:(const CLLocationCoordinate2D *)coords count:(NSUInteger)count; +/** + Replaces the vertices at the given range in the shape with the same number of + vertices from a given C array. If the shape is currently visible on the map, it + is redrawn immediately. + + The number of coordinates in `coords` must be equal to the length of `range`. + If you want to insert or delete one or more vertices, use the + `-replaceCoordinatesInRange:withCoordinates:count:` method. + + If `range` extends beyond the shape’s `coordinates` property, an + `NSRangeException` is raised. If you want to append new vertices to the shape, + use the `-appendCoordinates:count:` method. + + @param range The range of vertices to replace. The `location` field indicates + the first vertex you are replacing, with `0` being the first vertex, `1` + being the second vertex, and so on. The `length` field indicates the number + of vertices to replace. + @param coords The array of coordinates defining part of the shape. The data in + this array is copied to the shape’s `coordinate` property. + */ +- (void)replaceCoordinatesInRange:(NSRange)range withCoordinates:(const CLLocationCoordinate2D *)coords; + +/** + Replaces the vertices at the given range in the shape with the specified number + of vertices from a given C array. If the shape is currently visible on the map, + it is redrawn immediately. + + If `count` is greater than the `length` field of `range`, some vertices will + effectively be inserted into the shape. On the other hand, if `count` is less + than the `length` field of `range`, some vertices will effectively be removed. + + If `range` extends beyond the shape’s `coordinates` property, an + `NSRangeException` is raised. If you want to append new vertices to the shape, + use the `-appendCoordinates:count:` method. + + @param range The range of vertices to replace. The `location` field indicates + the first vertex you are replacing, with `0` being the first vertex, `1` + being the second vertex, and so on. The `length` field indicates the number + of vertices to replace. + @param coords The array of coordinates defining part of the shape. The data in + this array is copied to the shape’s `coordinates` property. + @param count The number of coordinates from the `coords` array to insert in + place of the coordinates in `range`. The sum of `range`’s length and this + count must not exceed the number of items currently in the `coordinates` + property. + */ +- (void)replaceCoordinatesInRange:(NSRange)range withCoordinates:(const CLLocationCoordinate2D *)coords count:(NSUInteger)count; + +/** + Removes the vertices at the given range from the shape. If the shape is + currently visible on the map, it is redrawn immediately. + + If `range` extends beyond the shape’s `coordinates` property, an + `NSRangeException` is raised. + + @param range The range of vertices to remove. The `location` field indicates + the first vertex you are removing, with `0` being the first vertex, `1` + being the second vertex, and so on. The `length` field indicates the number + of vertices to remove. + */ +- (void)removeCoordinatesInRange:(NSRange)range; + @end NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MGLMultiPoint.mm b/platform/darwin/src/MGLMultiPoint.mm index 57b57889f3..c49e970c6b 100644 --- a/platform/darwin/src/MGLMultiPoint.mm +++ b/platform/darwin/src/MGLMultiPoint.mm @@ -2,19 +2,12 @@ #import "MGLGeometry_Private.h" #import "MGLTypes.h" -#import - -mbgl::Color MGLColorObjectFromCGColorRef(CGColorRef cgColor) -{ - if (!cgColor) return { 0, 0, 0, 0 }; - NSCAssert(CGColorGetNumberOfComponents(cgColor) >= 4, @"Color must have at least 4 components"); - const CGFloat *components = CGColorGetComponents(cgColor); - return { (float)components[0], (float)components[1], (float)components[2], (float)components[3] }; -} +#include +#include @implementation MGLMultiPoint { - MGLCoordinateBounds _bounds; + mbgl::optional _bounds; std::vector _coordinates; } @@ -24,9 +17,11 @@ mbgl::Color MGLColorObjectFromCGColorRef(CGColorRef cgColor) if (self) { - NSAssert(count > 0, @"A multipoint must have coordinates"); + if (!count) { + [NSException raise:NSInvalidArgumentException + format:@"A multipoint must have at least one vertex."]; + } _coordinates = { coords, coords + count }; - [self computeBounds]; } return self; @@ -65,52 +60,87 @@ mbgl::Color MGLColorObjectFromCGColorRef(CGColorRef cgColor) std::copy(_coordinates.begin() + range.location, _coordinates.begin() + NSMaxRange(range), coords); } -- (void)appendCoordinates:(const CLLocationCoordinate2D *)coords count:(NSUInteger)count -{ +- (void)setCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count { + if (!count) { + [NSException raise:NSInvalidArgumentException + format:@"A multipoint must have at least one vertex."]; + } + + [self willChangeValueForKey:@"coordinates"]; + _coordinates = { coords, coords + count }; + _bounds = {}; + [self didChangeValueForKey:@"coordinates"]; +} + +- (void)insertCoordinates:(const CLLocationCoordinate2D *)coords count:(NSUInteger)count atIndex:(NSUInteger)index { + if (!count) { + return; + } + + if (index > _coordinates.size()) { + [NSException raise:NSRangeException + format:@"Invalid index %lu for existing coordinate count %ld", + (unsigned long)index, (unsigned long)[self pointCount]]; + } + [self willChangeValueForKey:@"coordinates"]; - _coordinates.insert(_coordinates.end(), count, *coords); - [self computeBounds]; + _coordinates.insert(_coordinates.begin() + index, count, *coords); + _bounds = {}; [self didChangeValueForKey:@"coordinates"]; } +- (void)appendCoordinates:(const CLLocationCoordinate2D *)coords count:(NSUInteger)count +{ + [self insertCoordinates:coords count:count atIndex:_coordinates.size()]; +} + - (void)replaceCoordinatesInRange:(NSRange)range withCoordinates:(const CLLocationCoordinate2D *)coords { - if (range.length == 0) - { + [self replaceCoordinatesInRange:range withCoordinates:coords count:range.length]; +} + +- (void)replaceCoordinatesInRange:(NSRange)range withCoordinates:(const CLLocationCoordinate2D *)coords count:(NSUInteger)count { + if (!count && !range.length) { return; } - if (NSMaxRange(range) > _coordinates.size()) - { + if (NSMaxRange(range) > _coordinates.size()) { [NSException raise:NSRangeException format:@"Invalid range %@ for existing coordinate count %ld", NSStringFromRange(range), (unsigned long)[self pointCount]]; } [self willChangeValueForKey:@"coordinates"]; - std::copy(coords, coords + range.length, _coordinates.begin() + range.location); - [self computeBounds]; + std::copy(coords, coords + MIN(count, range.length), _coordinates.begin() + range.location); + if (count >= range.length) { + _coordinates.insert(_coordinates.begin() + NSMaxRange(range), coords, coords + count - range.length); + } else { + _coordinates.erase(_coordinates.begin() + range.location + count, _coordinates.begin() + NSMaxRange(range)); + } + _bounds = {}; [self didChangeValueForKey:@"coordinates"]; } -- (void)computeBounds -{ - mbgl::LatLngBounds bounds = mbgl::LatLngBounds::empty(); - for (auto coordinate : _coordinates) - { - bounds.extend(mbgl::LatLng(coordinate.latitude, coordinate.longitude)); - } - _bounds = MGLCoordinateBoundsFromLatLngBounds(bounds); +- (void)removeCoordinatesInRange:(NSRange)range { + CLLocationCoordinate2D coords; + [self replaceCoordinatesInRange:range withCoordinates:&coords count:0]; } - (MGLCoordinateBounds)overlayBounds { - return _bounds; + if (!_bounds) { + mbgl::LatLngBounds bounds = mbgl::LatLngBounds::empty(); + for (auto coordinate : _coordinates) { + bounds.extend(mbgl::LatLng(coordinate.latitude, coordinate.longitude)); + } + _bounds = bounds; + } + return MGLCoordinateBoundsFromLatLngBounds(*_bounds); } - (BOOL)intersectsOverlayBounds:(MGLCoordinateBounds)overlayBounds { - return MGLLatLngBoundsFromCoordinateBounds(_bounds).intersects(MGLLatLngBoundsFromCoordinateBounds(overlayBounds)); + return MGLCoordinateBoundsIntersectsCoordinateBounds(self.overlayBounds, overlayBounds); } - (mbgl::Annotation)annotationObjectWithDelegate:(__unused id )delegate @@ -122,7 +152,8 @@ mbgl::Color MGLColorObjectFromCGColorRef(CGColorRef cgColor) - (NSString *)description { return [NSString stringWithFormat:@"<%@: %p; count = %lu; bounds = %@>", - NSStringFromClass([self class]), (void *)self, (unsigned long)[self pointCount], MGLStringFromCoordinateBounds(_bounds)]; + NSStringFromClass([self class]), (void *)self, (unsigned long)[self pointCount], + MGLStringFromCoordinateBounds(self.overlayBounds)]; } @end diff --git a/platform/darwin/src/MGLOverlay.h b/platform/darwin/src/MGLOverlay.h index 48d10d9de1..1066a86d1e 100644 --- a/platform/darwin/src/MGLOverlay.h +++ b/platform/darwin/src/MGLOverlay.h @@ -17,14 +17,6 @@ NS_ASSUME_NONNULL_BEGIN example, you could use an overlay to show the boundaries of a national park or trace a bus route along city streets. This SDK defines several concrete classes that conform to this protocol and define standard shapes. - - Because overlays are also annotations, they have similar usage pattern to - annotations. When added to a map view using the `-addOverlay:` method, that - view detects whenever the overlay’s defined region intersects the visible - portion of the map. At that point, the map view asks its delegate to provide a - special overlay view to draw the visual representation of the overlay. If you - add an overlay to a map view as an annotation instead, it is treated as an - annotation with a single point. */ @protocol MGLOverlay diff --git a/platform/darwin/src/MGLPointCollection.mm b/platform/darwin/src/MGLPointCollection.mm index f2bde38bc7..46b00b9a4d 100644 --- a/platform/darwin/src/MGLPointCollection.mm +++ b/platform/darwin/src/MGLPointCollection.mm @@ -8,10 +8,12 @@ NS_ASSUME_NONNULL_BEGIN @implementation MGLPointCollection { - MGLCoordinateBounds _bounds; + MGLCoordinateBounds _overlayBounds; std::vector _coordinates; } +@synthesize overlayBounds = _overlayBounds; + + (instancetype)pointCollectionWithCoordinates:(const CLLocationCoordinate2D *)coords count:(NSUInteger)count { return [[self alloc] initWithCoordinates:coords count:count]; @@ -28,7 +30,7 @@ NS_ASSUME_NONNULL_BEGIN { bounds.extend(mbgl::LatLng(coordinate.latitude, coordinate.longitude)); } - _bounds = MGLCoordinateBoundsFromLatLngBounds(bounds); + _overlayBounds = MGLCoordinateBoundsFromLatLngBounds(bounds); } return self; } @@ -61,14 +63,9 @@ NS_ASSUME_NONNULL_BEGIN std::copy(_coordinates.begin() + range.location, _coordinates.begin() + NSMaxRange(range), coords); } -- (MGLCoordinateBounds)overlayBounds -{ - return _bounds; -} - - (BOOL)intersectsOverlayBounds:(MGLCoordinateBounds)overlayBounds { - return MGLLatLngBoundsFromCoordinateBounds(_bounds).intersects(MGLLatLngBoundsFromCoordinateBounds(overlayBounds)); + return MGLCoordinateBoundsIntersectsCoordinateBounds(_overlayBounds, overlayBounds); } - (mbgl::Geometry)geometryObject diff --git a/platform/darwin/src/MGLPolygon.mm b/platform/darwin/src/MGLPolygon.mm index f4d999b98b..e5bb977863 100644 --- a/platform/darwin/src/MGLPolygon.mm +++ b/platform/darwin/src/MGLPolygon.mm @@ -99,7 +99,7 @@ } - (BOOL)intersectsOverlayBounds:(MGLCoordinateBounds)overlayBounds { - return MGLLatLngBoundsFromCoordinateBounds(_overlayBounds).intersects(MGLLatLngBoundsFromCoordinateBounds(overlayBounds)); + return MGLCoordinateBoundsIntersectsCoordinateBounds(_overlayBounds, overlayBounds); } - (mbgl::Geometry)geometryObject { diff --git a/platform/darwin/src/MGLPolyline.mm b/platform/darwin/src/MGLPolyline.mm index 5b6346f46a..ae6abc5280 100644 --- a/platform/darwin/src/MGLPolyline.mm +++ b/platform/darwin/src/MGLPolyline.mm @@ -79,7 +79,7 @@ } - (BOOL)intersectsOverlayBounds:(MGLCoordinateBounds)overlayBounds { - return MGLLatLngBoundsFromCoordinateBounds(_overlayBounds).intersects(MGLLatLngBoundsFromCoordinateBounds(overlayBounds)); + return MGLCoordinateBoundsIntersectsCoordinateBounds(_overlayBounds, overlayBounds); } - (mbgl::Geometry)geometryObject { -- cgit v1.2.1