Now that v8 has landed in https://github.com/mapbox/mapbox-gl-native/commit/0451aca37e866c3380072b114e2d49353888e4b5 we have a Circle layer type.
ref #1740
@incanus is there a roadmap plan for when MGLCircle will be implemented? Trying to update from the old "RMMapView" based SDK and the lack of replacement for an RMCircle is blocking me.
Thanks for the ping on this @cowgp. It hasn't been on the roadmap, but thinking this was relatively straightforward, I took a look today in 2167-MGLCircle.
The Cocoa side was pretty easy, but as it turns out, even though we have support for circles in the renderer and styles as of https://github.com/mapbox/mapbox-gl-native/commit/0451aca37e866c3380072b114e2d49353888e4b5, we don't have support for circle _annotations_ in that API.
It's an interesting bind, since circles seems like shapes (have alpha & color) but behave like points (have a single coordinate and a scalar icon-like property radius). So the going has been a little more than I expected there, but I think I'm close.
Stay tuned 鈥斅爓ill try to push this through soon.
/cc @friedbunny @jfirebaugh
@cowgp Sorry, I'm gonna have to say we're going to push this out a ways right now 鈥斅爐oo many higher priorities. If it was as easy as I had initially guessed, with pure Cocoa API, it would have been easy to rationalize, but increasing the surface area of our annotations API right now is not ideal given the other things we're integrating them with.
Stay tuned; I'd love to get this in, but it's not a priority roadmap right now.
@incanus - Thanks so much for looking into it, I appreciate the effort. I do hope it can remain a priority, seems like an important feature from my perspective. That said, totally understand that this update is all new underpinnings and thus there's a lot to build up.
@cowgp Depending on if your needs are truly at runtime, you could possibly use the existing support in the style spec for rendering circles in the meantime? https://github.com/mapbox/mapbox-gl-js/issues/1446 shows an example of how this looks in the style. You could also consider mutating the style JSON and custom setting it at runtime as a workaround.
@incanus - I looked into the style spec you linked, but no it doesn't seem to be a viable solve for our needs. We have ~45k active users each with one or more geofences that they can set/modify at will. This is accomplished in the existing deprecated SDK by adding an annotation at the center point of the geofence and then in the layerForAnnotation delegate method returning an RMCircle with the correct radius in meters for the fence. It seems to me a clear case where an MGLCircle is necessary to have in the new SDK.
What I meant about mutating the style, though, could be a workaround here. The idea would be that you'd have a parsed representation of the style JSON (as, say, an NSMutableDictionary) to which you could add/modify items in the layers array using the circle primitive. Then, you'd send this out to JSON data with NSJSONSerialization and use it to set the styleJSON in the library.
Admittedly more work than just having MGLCircle, but an idea.
Is this still not available with the new release of mapbox studio?
Nope, no further movement here just yet. Workaround is to code circle primitives into your style and/or add them to the style JSON at runtime for now.
@incanus can you please provide an example of this workaround?
Ah, yes, this would require something like the GeoJSON source in #2161 in order to modify runtime data for the circle layer(s). In that case, I would recommend instead adding UIView objects atop the map for now and modifying this routine to update each's placement using the same call as here to find the new screen center point based on circle center coordinate and using -metersPerPixelAtLatitude to determine radius on screen. It's not ideal, but a stopgap for now.
I take that back 鈥斅爐his could also be accomplished with the existing annotations API, with the slight downside that unlike a true MGLCircle, it must be layered atop all the other map layers (and below any point annotations). However, that aside, it's pretty trivial to do. Here's a sample project that pulls a GeoJSON with points, parses it, picks the first 50, and adds point annotations using imagery generated on-the-fly with Core Graphics having random radius and color (reusing sprites efficiently where available).

/cc @friedbunny @jfirebaugh @brunoabinader @tmcw
Of course the other main hack here is that circle radii are only valid for a single zoom since they don't scale as the map does. You could combat this by pre-generating circles of varying radii for given integer zoom levels and only allowing external control to zoom to each, or else disallowing map zoom once you've added a particular image. Either way you'd be using MGLMapView.metersPerPixelAtLatitude() to determine the draw radius for the current zoom.
@incanus - Thanks for showing a functional work around - helpful for sure. The performance hit when zooming is significant - and disabling zoom is not an option for me. That loops me back to your original suggestion, I haven't tried it yet, but I was about to - mutating the style that is. You've made subsequent suggestions here and a POC workaround that did not head down that path. Is there anything wrong with still pursuing circles in the style JSON?
@cowgp It should work, the trick is updating your vector tile source data rapidly enough. You would put circle primitives in your style JSON directly and reapply the mapView.styleURL to update the appearance (which itself would be a little dramatic visually, but could work for a prep, load, then reveal scenario). The trick is you can only put those circles where there are data points in the tiles, and so you would need to do something like upload your GeoJSON or other source the Mapbox backend and then reload it to the client. Or else manually hack a solution with the underlying AnnotationTile live-tile structures used for point/shape annotations, but at that point you're in the territory that I was towards building MGLCircle anyway.
@incanus - I've initially implemented my work around in a similar manner to the sample you provided, but I've hit a snag and am now banging my head. using a standard MGLPointAnnotation and providing the circle as a UIImage does a fine job of simply rendering a circle... our use case is a radial geofence, which unfortunately means that the circle needs to adjust it's size correctly when the zoom level on the map changes. the head banging comes in that I have tried implementing both:
- (void)mapViewRegionIsChanging:(MGLMapView *)mapView
- (void)mapView:(MGLMapView *)mapView regionDidChangeAnimated:(BOOL)animated
as trigger points to try and force calculate/draw a new circle based on the new metersPerPixel value of the geofence annotation, but the annotation intermittently is not displayed/rendered. To prove it is not my circle drawing code, I just used a standard PNG image and it similarly would only appear intermittently.
The trick is that to get a new image displayed for the annotation, you have to somehow get - mapView: imageForAnnotation: to be called for that annotation. I first tried removing the annotation and then adding it right back, then tried removing the annotation and alloc/init-ing a new one in it's place. The later will consistently trigger imageForAnnotation: and I provide an image, whether that is a static PNG or a custom drawn circle at runtime, but as I said above, it only appears on the screen some of the time even though the method always returns something.
Do you have a better trick for forcing imageForAnnotation: to be called so I can provide resized circles?
@cowgp Can you model this using Polygon annotation? A polygon with 50 or so edges will very closely match a circle.
@mb12 - the polygon is a much much better solve than rendering circles as images and struggling with the zoom. I went with 45 sides (evenly divisible into 360) and it's reasonably performant and it correctly scales when the map is zoomed. Thanks a bunch for the suggestion. For anyone else following along, below is my method, and what seems to be the best work around for current lack of MGLCircle.
- (MGLPolygon*)polygonCircleForCoordinate:(CLLocationCoordinate2D)coordinate withMeterRadius:(double)meterRadius
{
NSUInteger degreesBetweenPoints = 8; //45 sides
NSUInteger numberOfPoints = floor(360 / degreesBetweenPoints);
double distRadians = meterRadius / 6371000.0; // earth radius in meters
double centerLatRadians = coordinate.latitude * M_PI / 180;
double centerLonRadians = coordinate.longitude * M_PI / 180;
CLLocationCoordinate2D coordinates[numberOfPoints]; //array to hold all the points
for (NSUInteger index = 0; index < numberOfPoints; index++) {
double degrees = index * degreesBetweenPoints;
double degreeRadians = degrees * M_PI / 180;
double pointLatRadians = asin( sin(centerLatRadians) * cos(distRadians) + cos(centerLatRadians) * sin(distRadians) * cos(degreeRadians));
double pointLonRadians = centerLonRadians + atan2( sin(degreeRadians) * sin(distRadians) * cos(centerLatRadians),
cos(distRadians) - sin(centerLatRadians) * sin(pointLatRadians) );
double pointLat = pointLatRadians * 180 / M_PI;
double pointLon = pointLonRadians * 180 / M_PI;
CLLocationCoordinate2D point = CLLocationCoordinate2DMake(pointLat, pointLon);
coordinates[index] = point;
}
MGLPolygon *polygon = [MGLPolygon polygonWithCoordinates:coordinates count:numberOfPoints];
return polygon;
}
The delegate methods for stroke color and fill color are then called for you to set your colors:
- (UIColor *)mapView:(MGLMapView *)mapView strokeColorForShapeAnnotation:(MGLShape *)annotation
- (UIColor *)mapView:(MGLMapView *)mapView fillColorForPolygonAnnotation:(MGLPolygon *)annotation
It is unfortunate however that supplying a color with less than 1 alpha value for the fill does weird things with the GL blend mode. your only option is to return the alpha in the delegate method:
- (CGFloat)mapView:(MGLMapView *)mapView alphaForShapeAnnotation:(MGLShape *)annotation
which applies the alpha to the stroke as well as the fill. In an ideal scenario, I'd be able to have a semi-transparent fill with an opaque stroke. Regardless, the above polygon hack gets me way closer to functional that I was before.

@cowgp thanks!
Rewrote your version to swift 2.0
func polygonCircleForCoordinate(coordinate: CLLocationCoordinate2D, withMeterRadius: Double) {
let degreesBetweenPoints = 8.0
let numberOfPoints = floor(360.0 / degreesBetweenPoints)
let distRadians: Double = withMeterRadius / 6371000.0
let centerLatRadians: Double = coordinate.latitude * M_PI / 180
let centerLonRadians: Double = coordinate.longitude * M_PI / 180
var coordinates = [CLLocationCoordinate2D]()
for var index = 0; index < Int(numberOfPoints); index++ {
let degrees: Double = Double(index) * Double(degreesBetweenPoints)
let degreeRadians: Double = degrees * M_PI / 180
let pointLatRadians: Double = asin(sin(centerLatRadians) * cos(distRadians) + cos(centerLatRadians) * sin(distRadians) * cos(degreeRadians))
let pointLonRadians: Double = centerLonRadians + atan2(sin(degreeRadians) * sin(distRadians) * cos(centerLatRadians), cos(distRadians) - sin(centerLatRadians) * sin(pointLatRadians))
let pointLat: Double = pointLatRadians * 180 / M_PI
let pointLon: Double = pointLonRadians * 180 / M_PI
let point: CLLocationCoordinate2D = CLLocationCoordinate2DMake(pointLat, pointLon)
coordinates.append(point)
}
let polygon = MGLPolygon(coordinates: &coordinates, count: UInt(coordinates.count))
mapview.addAnnotation(polygon)
}
thanx @Lengo46 for the swiftcode! Brilliant stuff!
@cowgp Thanks man!
I rewrote it for Android!
private ArrayList<LatLng> polygonCircleForCoordinate(LatLng location, double radius){
int degreesBetweenPoints = 8; //45 sides
int numberOfPoints = (int) Math.floor(360 / degreesBetweenPoints);
double distRadians = radius / 6371000.0; // earth radius in meters
double centerLatRadians = location.getLatitude() * Math.PI / 180;
double centerLonRadians = location.getLongitude() * Math.PI / 180;
ArrayList<LatLng> polygons = new ArrayList<>(); //array to hold all the points
for (int index = 0; index < numberOfPoints; index++) {
double degrees = index * degreesBetweenPoints;
double degreeRadians = degrees * Math.PI / 180;
double pointLatRadians = Math.asin(Math.sin(centerLatRadians) * Math.cos(distRadians) + Math.cos(centerLatRadians) * Math.sin(distRadians) * Math.cos(degreeRadians));
double pointLonRadians = centerLonRadians + Math.atan2(Math.sin(degreeRadians) * Math.sin(distRadians) * Math.cos(centerLatRadians),
Math.cos(distRadians) - Math.sin(centerLatRadians) * Math.sin(pointLatRadians));
double pointLat = pointLatRadians * 180 / Math.PI;
double pointLon = pointLonRadians * 180 / Math.PI;
LatLng point = new LatLng(pointLat, pointLon);
polygons.add(point);
}
return polygons;
}
Add to your mapboxMap by using
mapboxMap.addPolygon(new PolygonOptions().addAll(polygonCircleForCoordinate(point, 500.0)).fillColor(Color.parseColor("#12121212")));
@cowgp Also many thanks!
I rewrote it for RubyMotion:
def polygon_circle(coordinates, radius)
degrees_between_points = 8.0
number_of_points = (360.0/degrees_between_points).floor
dist_radians = radius / 6371000.0
center_lat_radians = coordinates.latitude * Math::PI / 180
center_lon_radians = coordinates.longitude * Math::PI / 180
polygon_coordinates = Array.new()
for index in 0...number_of_points
degrees = index * degrees_between_points
degree_radians = degrees * Math::PI / 180
point_lat_radians = Math.asin(Math.sin(center_lat_radians) * Math.cos(dist_radians) + Math.cos(center_lat_radians) * Math.sin(dist_radians) * Math.cos(degree_radians))
point_lon_radians = center_lon_radians + Math.atan2(Math.sin(degree_radians) * Math.sin(dist_radians) * Math.cos(center_lat_radians), Math.cos(dist_radians) - Math.sin(center_lat_radians) * Math.sin(point_lat_radians))
point_lat = point_lat_radians * 180 / Math::PI
point_lon = point_lon_radians * 180 / Math::PI
point = CLLocationCoordinate2DMake(point_lat, point_lon)
polygon_coordinates << point
end
polygon_coordinates_ptr = Pointer.new(CLLocationCoordinate2D.type, polygon_coordinates.length)
polygon_coordinates.each_index { |index| polygon_coordinates_ptr[index] = polygon_coordinates[index] }
polygon = MGLPolygon.polygonWithCoordinates(polygon_coordinates_ptr, count: polygon_coordinates.length)
polygon
end
I merged the two examples with swift example here ->
https://github.com/johndpope/CircleAnnotationStopgap
Beware - I'm seeing some memory leaks on this method in conjunction with clustering manager. (no time to fix this)
func circleImageWithRadius(radius: Int, color: UIColor) -> UIImage

This probably deserves a new ticket - but did anyone have a crack at adding heatmap overlays?
looking at the code to do the polygon overlays / uiimages - seems feasible.
I'm looking for a shortcut.
In this example - they are using GMSGroundOverlay ontop of GMSMapView
https://github.com/kenzan8000/Avoid-Crime
let overlay = GMSGroundOverlay(
position: self.projection.coordinateForPoint(CGPointMake(self.frame.size.width / 2.0, self.frame.size.height / 2.0)),
icon: UIImage.heatmapImage(map: self, crimes: drawingCrimes),
zoomLevel: CGFloat(self.camera.zoom)
)
class func heatmapImage(map map: GMSMapView, crimes: [DACrime]) -> UIImage {
var locations: [CLLocation] = []
var weights: [NSNumber] = []
for crime in crimes {
let lat = crime.lat.doubleValue
let long = crime.long.doubleValue
locations.append(CLLocation(latitude: lat, longitude: long))
var weight = DASFGovernment.Crime.Weights_number[crime.category]
if weight == nil { weight = DASFGovernment.Crime.Weights_number["THE OTHERS"] }
weights.append(weight!)
}
var points: [NSValue] = []
for var i = 0; i < locations.count; i++ {
let location = locations[i]
points.append(NSValue(CGPoint: map.projection.pointForCoordinate(location.coordinate)))
}
let image = DACrimeHeatmap.crimeHeatmapWithRect(
map.frame,
boost: 1.0,
points: points,
weights: weights
)
return image
}
this appears that an entire uiimage is dropped onto the google maps overlay. re-rendering this when need be. Did anyone attempt this with mapbox?

Yeah, I'm also in a scenario where I need to indicate intensity radiating out from a central location; some visual meaning is lost with a solid colored overlay for me. I might have to fix zoom level for now...
This also probably deserves a new ticket, or not, since it is more like a question than a particular issue, the issue would be to add some helper method to the API to do this, and with this I mean, if anyone has ever needed to calculate the maximum required zoomLevel to display an entire circle centered in the map given its radius and its center position?
@mgtitimoli, that's worth filing a separate issue over. If we add any API like that, it'd probably be more general, to compute the map camera needed to show an annotation of any kind. But if you need to know this information for a shape you haven't added to the map yet, this might be a better role for a generalized geometry library than this SDK.
To be sure, there are already methods for fitting the map to a given annotation already on the map (-showAnnotations:animated:).
For those interested in drawing a circle whose radius corresponds to a screen distance rather than a geographic coordinate span, check out MGLCircleStyleLayer (in conjunction with MGLGeoJSONSource MGLShapeSource) in the latest iOS SDK alpha.
Now, with runtime styling, MGLCircleStyleLayer is able to created, modified, and added at runtime. Might meet most use cases here.
MGLCircleStyleLayer only creates circles whose radii correspond to screen distances measured in points. For parity with MKCircle (MapKit) and GMSCircle (Google Maps SDK), we still need an MGLCircle whose radius corresponds to a real-world distance measured in meters.
we still need an MGLCircle whose radius corresponds to a real-world distance measured in meters.
FWIW that's the same issue I have with Qt MapCircle items.
Two stopgap ideas:
import UIKit
import Mapbox
class ViewController: UIViewController, MGLMapViewDelegate {
var map: MGLMapView!
let center = CLLocationCoordinate2D(latitude: 45, longitude: -122)
let radiusMeters = 1000.0
override func viewDidLoad() {
super.viewDidLoad()
map = MGLMapView(frame: view.bounds)
map.setCenter(center, zoomLevel: 10, animated: false)
map.styleURL = MGLStyle.lightStyleURL(withVersion: MGLStyleDefaultVersion)
map.delegate = self
view.addSubview(map)
}
func mapView(_ mapView: MGLMapView, didFinishLoading style: MGLStyle) {
let point = MGLPointAnnotation()
point.coordinate = center
let source = MGLShapeSource(identifier: "circle", shape: point, options: nil)
style.addSource(source)
let layer = MGLCircleStyleLayer(identifier: "circle", source: source)
let radiusPoints = radiusMeters / mapView.metersPerPoint(atLatitude: center.latitude)
layer.circleRadius = MGLStyleValue(rawValue: NSNumber(value: radiusPoints))
layer.circleColor = MGLStyleValue(rawValue: UIColor.red)
layer.circleOpacity = MGLStyleValue(rawValue: 0.5)
style.addLayer(layer)
}
func mapViewRegionIsChanging(_ mapView: MGLMapView) {
if let layer = mapView.style?.layer(withIdentifier: "circle") as? MGLCircleStyleLayer {
let radiusPoints = radiusMeters / mapView.metersPerPoint(atLatitude: center.latitude)
layer.circleRadius = MGLStyleValue(rawValue: NSNumber(value: radiusPoints))
}
}
}
Seems pretty performant to me.
Here's a quick high FPS video on device to demo:
As with geodesic polylines https://github.com/mapbox/mapbox-gl-native/issues/1726, this could be approximated with polygon layers formed out of ~100 segments (technically a _centagon_ 馃槺).
If you鈥檙e writing your application in Swift, turf-swift鈥檚 coordinate(at:facing:) method comes in handy for this workaround.
Hello - Nadia from Mapbox support pointed us towards https://github.com/mapbox/mapbox-gl-native/issues/2167#issuecomment-169265375 as a workaround for our need for this feature.
I'm definitely putting in a feature request for this.
Here are a couple of iOS apps that do this: It is implemented as a feature in Citymapper (approximate walking isochrone) using Google Maps and in Tile app which using Apple Maps.
Thanks to @Ruben2112, here is a Kotlin conversion from your code:
private fun polygonCircleForCoordinate(location: LatLng, radius: Double): ArrayList<LatLng> {
val degreesBetweenPoints = 8 //45 sides
val numberOfPoints = Math.floor((360 / degreesBetweenPoints).toDouble()).toInt()
val distRadians = radius / 6371000.0 // earth radius in meters
val centerLatRadians = location.latitude * Math.PI / 180
val centerLonRadians = location.longitude * Math.PI / 180
val polygons = arrayListOf<LatLng>() //array to hold all the points
for (index in 0 until numberOfPoints) {
val degrees = (index * degreesBetweenPoints).toDouble()
val degreeRadians = degrees * Math.PI / 180
val pointLatRadians = Math.asin(Math.sin(centerLatRadians) * Math.cos(distRadians) + Math.cos(centerLatRadians) * Math.sin(distRadians) * Math.cos(degreeRadians))
val pointLonRadians = centerLonRadians + Math.atan2(Math.sin(degreeRadians) * Math.sin(distRadians) * Math.cos(centerLatRadians),
Math.cos(distRadians) - Math.sin(centerLatRadians) * Math.sin(pointLatRadians))
val pointLat = pointLatRadians * 180 / Math.PI
val pointLon = pointLonRadians * 180 / Math.PI
val point = LatLng(pointLat, pointLon)
polygons.add(point)
}
return polygons
}
Add to mapboxMap:
mapboxMap!!.addPolygon(PolygonOptions()
.addAll(polygonCircleForCoordinate(alertPoint.latLng, alertPoint.radius!!))
.fillColor(colorAlertPoint)
.strokeColor(colorAlertStroke))
This issue is specifically about supporting georeferenced circles in the iOS SDK (and macOS SDK). #4312 tracks the same thing for the Android SDK.
Am I correct in thinking there's still no parity with MKCircle in MapBox? Seems like a glaring omission to me.
Edit: one of the proposed solutions (https://github.com/mapbox/mapbox-gl-native/issues/2167#issuecomment-278834642) doesn't even compile anymore, and crashes when changed to use NSExpression.
#2167 (comment) Actually works. I just changed MGLStyleValue to NSExpression(forConstantValue: xxx)
This issue has been automatically detected as stale because it has not had recent activity and will be archived. Thank you for your contributions.
This issue has been automatically detected as stale because it has not had recent activity and will be archived. Thank you for your contributions.
Any news on this front? (trying to get the bot not to close it again)
Given it鈥檚 3 years old, would be nice to have some sort of update on the issue for sure.
@jlubeck @KaneCheshire just wanted to assure you that this is on our list - and you should start seeing some movement on this issue soon.
Thanks for the update @julianrex
Great to hear that @julianrex !
@jlubeck @KaneCheshire - After some internal discussions we are currently planning a major refactor to how shapes are generated to be added to a map. The current plan is to wait until that work is done before proceeding to address this issue. I know this is not ideal, but we believe the upcoming refactoring we鈥檙e working on will make adding all sorts of shapes to a map much easier for everyone in the long run. We鈥檒l be sure to post back here as soon as we have more updates.
If you're looking for a workaround right now, one possibility would be to implement this with turf-swift. There are limitations to this approach though - this is not a true circle, but rather a an n-sided polygon (I just used the default 64 from the Turf.js documentation. Here is an example of my implementation. In addition, you can also update the shape of this circle on-the-fly using an MGLMapViewDelegate method that responds to map position changes.
Thanks for the heads up @captainbarbosa
How's the refactoring going? This issue will be 4 years old in just over a month :/
All, thanks for following this ticket. Our refactor is going well, and we are nearing a go/no-go decision on merging https://github.com/mapbox/mapbox-gl-native/pull/14534 sometime this year after we resolve the remaining considerations.
Please upvote this comment if https://github.com/mapbox/mapbox-gl-native/pull/14534 would work for your use case and solve your needs.
Per its description, the PR implements a new MGLCircle class, analogous to MapKit鈥檚 MKCircle or the Google Map SDK鈥檚 GMSCircle, which allows the developer to add spherical caps to the map view as overlays or to a shape source as polygons.
Any news on this issue? Looks like it was on good track back in August... And Github is returning a 500 error on the pull request so I can't see if there is progress there... Weird thing is that is the only PR that is returning an error, the others work just fine
Thanks!
Inspired by @incanus's answer in 2017, I came up with another solution, which doesn't requires usage of MGLCircleStyleLayer, but uses mapView(_:viewFor annotation:) -> MGLAnnotationView? delegate method.
Code below is written in Swift 5, using Mapbox-iOS-SDK 6.1.0, The performance seems ok on a simulator.
// Code below only covers 1 annotation use case for demo purpose, make changes if you need to support multiple annotations
class MyMapBoxViewController: UIViewController, MGLMapViewDelegate {
/// Use this stored property to track my circle
private var circleAnnotationView: MGLAnnotationView?
private let radiusMeters: Double = 200
private let divisor: Double = 2
...
/// Return a clear image instead of default pin image, so the annotation will be invisible.
/// if you do not want to hide annotation, comment this method out
func mapView(_ mapView: MGLMapView, imageFor annotation: MGLAnnotation) -> MGLAnnotationImage? {
let clearImage = mapView.dequeueReusableAnnotationImage(withIdentifier: "clear") ??
.init(image: UIImage(color: .clear)!, reuseIdentifier: "clear")
return clearImage
}
/// Return a circular `MGLAnnotationView` for the annotation, use ViewController's stored property `circleAnnotationView` to track it
func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? {
// use lng, lat to create a unique reuse identifier
let reuseIdentifier = "\(annotation.coordinate.longitude)-\(annotation.coordinate.latitude)"
// reuse or create a circle view around the annotation
circleAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseIdentifier) ??
CustomAnnotationView(reuseIdentifier: reuseIdentifier)
var radiusPoints = radiusMeters / mapView.metersPerPoint(atLatitude: annotation.coordinate.latitude)
radiusPoints /= divisor // divide by (2) here so circle doesn't fill the entire screen, please choose your own divisor
circleAnnotationView?.bounds = .init(origin: .zero, size: .init(width: radiusPoints, height: radiusPoints))
circleAnnotationView?.backgroundColor = UIColor.blue.withAlphaComponent(0.5)
return circleAnnotationView
}
/// When map zooms, rescale `circleAnnotationView` based on zoom level
func mapViewRegionIsChanging(_ mapView: MGLMapView) {
guard circleAnnotationView != nil else { return }
var radiusPoints = radiusMeters / mapView.metersPerPoint(atLatitude: mapView.centerCoordinate.latitude)
radiusPoints /= divisor // divide by (2) here so circle doesn't fill the entire screen, please choose your own divisor
circleAnnotationView?.bounds = .init(origin: .zero, size: .init(width: radiusPoints, height: radiusPoints))
}
}
Some subclass and extension used in code above
class CustomAnnotationView: MGLAnnotationView {
override func layoutSubviews() {
super.layoutSubviews()
// Use CALayer鈥檚 corner radius to turn this view into a circle.
layer.cornerRadius = bounds.width / 2
layer.borderWidth = 2
layer.borderColor = UIColor.textBlue.cgColor
}
}
extension UIImage {
/// Use color to init an `UIImage`
convenience init?(color: UIColor, size: CGSize = CGSize(width: 1, height: 1)) {
let rect = CGRect(origin: .zero, size: size)
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
color.setFill()
UIRectFill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
guard let cgImage = image?.cgImage else { return nil }
self.init(cgImage: cgImage)
}
}
Most helpful comment
@cowgp Thanks man!
I rewrote it for Android!
Add to your mapboxMap by using