[ios, macos] More ways to reshape an MGLMultiPoint (#7251)

* [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.
This commit is contained in:
Minh Nguyễn
2016-12-06 23:36:55 -08:00
committed by GitHub
parent 9f9422deb8
commit 4c2aec2355
9 changed files with 179 additions and 85 deletions

View File

@@ -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 &&

View File

@@ -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;
/**
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.
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 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

View File

@@ -2,19 +2,12 @@
#import "MGLGeometry_Private.h"
#import "MGLTypes.h"
#import <mbgl/util/geo.hpp>
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 <mbgl/util/geo.hpp>
#include <mbgl/util/optional.hpp>
@implementation MGLMultiPoint
{
MGLCoordinateBounds _bounds;
mbgl::optional<mbgl::LatLngBounds> _bounds;
std::vector<CLLocationCoordinate2D> _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)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.begin() + index, count, *coords);
_bounds = {};
[self didChangeValueForKey:@"coordinates"];
}
- (void)appendCoordinates:(const CLLocationCoordinate2D *)coords count:(NSUInteger)count
{
[self willChangeValueForKey:@"coordinates"];
_coordinates.insert(_coordinates.end(), count, *coords);
[self computeBounds];
[self didChangeValueForKey:@"coordinates"];
[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 <MGLMultiPointDelegate>)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

View File

@@ -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 <MGLAnnotation>

View File

@@ -8,10 +8,12 @@ NS_ASSUME_NONNULL_BEGIN
@implementation MGLPointCollection
{
MGLCoordinateBounds _bounds;
MGLCoordinateBounds _overlayBounds;
std::vector<CLLocationCoordinate2D> _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<double>)geometryObject

View File

@@ -99,7 +99,7 @@
}
- (BOOL)intersectsOverlayBounds:(MGLCoordinateBounds)overlayBounds {
return MGLLatLngBoundsFromCoordinateBounds(_overlayBounds).intersects(MGLLatLngBoundsFromCoordinateBounds(overlayBounds));
return MGLCoordinateBoundsIntersectsCoordinateBounds(_overlayBounds, overlayBounds);
}
- (mbgl::Geometry<double>)geometryObject {

View File

@@ -79,7 +79,7 @@
}
- (BOOL)intersectsOverlayBounds:(MGLCoordinateBounds)overlayBounds {
return MGLLatLngBoundsFromCoordinateBounds(_overlayBounds).intersects(MGLLatLngBoundsFromCoordinateBounds(overlayBounds));
return MGLCoordinateBoundsIntersectsCoordinateBounds(_overlayBounds, overlayBounds);
}
- (mbgl::Geometry<double>)geometryObject {

View File

@@ -46,7 +46,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT
### Annotations
* Added new methods to MGLMultiPoint for changing or appending vertices along polyline annotations and the exteriors of polygon annotations. ([#6565](https://github.com/mapbox/mapbox-gl-native/pull/6565))
* Added new methods to MGLMultiPoint for changing the vertices along a polyline annotation or the exterior of a polygon annotation. ([#6565](https://github.com/mapbox/mapbox-gl-native/pull/6565))
* Added new APIs to MGLMapView to query for visible annotations. Combined with `-[MGLMapView viewForAnnotation:]`, these APIs can be used to access all visible annotation views. ([6061](https://github.com/mapbox/mapbox-gl-native/pull/6061))
* Fixed an issue causing offscreen annotation views to be updated even when they were in the reuse queue. ([#5987](https://github.com/mapbox/mapbox-gl-native/pull/5987))
* Fixed an issue preventing MGLAnnotationView from animating when its coordinate changes. ([#6215](https://github.com/mapbox/mapbox-gl-native/pull/6215))

View File

@@ -32,7 +32,7 @@
### Annotations
* Added `showAnnotations:animated:` and `showAnnotations:edgePadding:animated:`, which moves the map viewport to show the specified annotations. ([#5749](https://github.com/mapbox/mapbox-gl-native/pull/5749))
* Added new methods to MGLMultiPoint for changing or appending vertices along polyline annotations and the exteriors of polygon annotations. ([#6565](https://github.com/mapbox/mapbox-gl-native/pull/6565))
* Added new methods to MGLMultiPoint for changing the vertices along a polyline annotation or the exterior of a polygon annotation. ([#6565](https://github.com/mapbox/mapbox-gl-native/pull/6565))
* Added new APIs to MGLMapView to query for visible annotations. ([6061](https://github.com/mapbox/mapbox-gl-native/pull/6061))
* Deprecated `-[MGLMapViewDelegate mapView:alphaForShapeAnnotation:]` in favor of specifying an alpha component via `-[MGLMapViewDelegate mapView:strokeColorForShapeAnnotation:]` or `-[MGLMapViewDelegate mapView:fillColorForPolygonAnnotation:]`. ([#6706](https://github.com/mapbox/mapbox-gl-native/pull/6706))
* Various method arguments that are represented as C arrays of `CLLocationCoordinate2D` instances have been marked `const` to streamline bridging to Swift. ([#7215](https://github.com/mapbox/mapbox-gl-native/pull/7215))