Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Add API for managing a style’s icon images #6436

Closed
1ec5 opened this issue Sep 22, 2016 · 11 comments · Fixed by #6637
Closed

Add API for managing a style’s icon images #6436

1ec5 opened this issue Sep 22, 2016 · 11 comments · Fixed by #6637
Labels
feature iOS Mapbox Maps SDK for iOS macOS Mapbox Maps SDK for macOS runtime styling
Milestone

Comments

@1ec5
Copy link
Contributor

1ec5 commented Sep 22, 2016

MGLStyle should have methods for managing a style’s images based on the mbgl support added in #6375. This will make it easier to work with style properties such as iconImage.

We could either implement -imageNames, -image:forName:, and -setImage:forName: or a single, mutable imagesByName dictionary property along the lines of #6097. Images would be straight-up NSImages (on macOS) or UIImages (on iOS). Style images are very closely related to MGLAnnotationImage.image, but I don’t think we should tangle this API with MGLAnnotationImage at all, because MGLAnnotationImage is sort of a proxy view object while style images only last for the lifetime of the style.

/cc @incanus @frederoni

@1ec5 1ec5 added feature iOS Mapbox Maps SDK for iOS macOS Mapbox Maps SDK for macOS runtime styling labels Sep 22, 2016
@1ec5 1ec5 added this to the ios-v3.4.0 milestone Sep 22, 2016
@1ec5 1ec5 changed the title Add API for managing a style’s images Add API for managing a style’s icon images Sep 22, 2016
@rmnblm
Copy link

rmnblm commented Sep 25, 2016

I am facing this problem right now. Styling the points with a MGLCircleStyleLayer works perfectly:

let geoJSONURL = Bundle.main.url(forResource: "siliconvalley", withExtension: "geojson")!

let geoJSONSource = MGLGeoJSONSource(sourceIdentifier: "sv", url: geoJSONURL)
mapView.style().add(geoJSONSource)

let cafeStyleLayer = MGLCircleStyleLayer(layerIdentifier: "sv-cafe-layer", sourceIdentifier: "sv")!
cafeStyleLayer.predicate = NSPredicate(format: "amenity == 'cafe'")
cafeStyleLayer.circleColor = UIColor.brown
mapView.style().add(cafeStyleLayer)

...

I tried to replace the MGLCircleStyleLayer with a MGLSymbolStyleLayer and set the iconImage property with a string (the name of an image in my Assets folder) but couldn't get it to work, I'm getting the following error:

{Worker}[Sprite]: Can't find sprite named 'cafe.pdf'

One possible solution: I could parse the GeoJSON and adding the points manually and filter each annotation inside the delegate method public func mapView(_ mapView: MGLMapView, imageFor annotation: MGLAnnotation) -> MGLAnnotationImage?. Any other ideas?

@1ec5
Copy link
Contributor Author

1ec5 commented Sep 26, 2016

Note that iconImage is actually the name of a sprite in the style's sprite sheet, not any arbitrary image in the resource bundle. The feature proposed here would add a way to add an image from your resource bundle to the sprite sheet programmatically.

In the meantime, you could use point annotations if you're OK with the differences in behavior.

@rmnblm
Copy link

rmnblm commented Sep 26, 2016

If I use point annotations I'd lose the support of clustering, which means, I can only have clustering support with style layers without custom marker or no clustering without style layer with custom markers, right? 🙈

Which leads me to the question on how you're going to implement this proposed feature: Which iconImage are you going to display on a clustered point?

@1ec5
Copy link
Contributor Author

1ec5 commented Sep 26, 2016

Ah, if you need clustering, then annotations aren’t an option yet: #5814. We don’t expect to implement that in time for the v3.4.0 release, so the short answer to your question is that clustering uses the iconImage property, which is a sprite name rather than a file name. (See also #6098, wherein I complain about this confusing property name.)

It is currently possible to take advantage of the fact that annotations are implemented using ordinary style layers. It’s a real kludge, but as a workaround, you could force your own images to be installed as sprites and use them for clustered GeoJSON points: add a dummy MGLPointAnnotation for each image you want to install, so that you can provide an MGLAnnotationImage; from then on, you can use that image as an iconImage or backgroundPattern. Prepend com.mapbox.sprites. to the MGLAnnotationImage’s reuse identifier to get the sprite name.

Note that this workaround relies on several implementation details that can and probably will change without notice in a future release. If you can wait for this issue to be resolved, simply calling -[MGLStyle setImage:forName:] would be much better. 😄

@nitrag
Copy link
Contributor

nitrag commented Sep 27, 2016

+1 on this!

Sprite: So if I have an image, how is it converted to a (vector?) sprite? Quality / performance degradations/improvements in doing this? How does it know the appropriate size?

iconImage vs clusterImage. I've seen other SDK's have ability to have a different cluster view. Either bubbles, bubbles /w text, or a different image than the marker/annotation themselves.

While simple and performant, circle annotations aren't that pretty/usable in some real life apps and I'd very much like to see this implemented!

@1ec5
Copy link
Contributor Author

1ec5 commented Sep 27, 2016

Sprite: So if I have an image, how is it converted to a (vector?) sprite? Quality / performance degradations/improvements in doing this? How does it know the appropriate size?

Sprites are raster images. They’re baked into the style, or you can provide them at runtime via MGLAnnotationImage for point annotations. The proposed API will allow you to add sprites at runtime for more general usage than just annotations. The quality and performance characteristics are equivalent to MGLAnnotationImage usage, except that sprites get wiped away when you switch styles. No resizing takes place.

iconImage vs clusterImage. I've seen other SDK's have ability to have a different cluster view. Either bubbles, bubbles /w text, or a different image than the marker/annotation themselves.

iconImage (which is already implemented) is equivalent to the icon-image property in the style specification. Sprites are also used for other properties, such as fillPattern (fill-pattern). No style specification changes will be made within the scope of this iOS/macOS-specific issue. The ability to mark a cluster of annotations with a view is tracked by #5815.

Nevertheless, as demonstrated by this GL JS example, it’s already possible to make a cluster look different than the individual points it represents. A bubble or bubble with text would use an MGLCircleStyleLayer, while a point-placed image would require an MGLSymbolStyleLayer instead.

@rmnblm
Copy link

rmnblm commented Sep 27, 2016

So I tried your workaround @1ec5 but still get Can't find sprite named 'custom-bank'

Here's how I implemented it:

  1. I added a PNG image to the Assets.xcassets folder (actually, I added two images, one 1x and one 2x). Let's name this image "custom-bank".

  2. In the viewDidLoad() method, I'm adding a dummy annotation:

    let point = MGLPointAnnotation()
    point.title = "custom-bank"
    mapView.addAnnotation(point)
    
  3. The delegate function gets called

    public func mapView(_ mapView: MGLMapView, imageFor annotation: MGLAnnotation) -> MGLAnnotationImage? {
        let reuseIdentifier = "com.mapbox.sprites.\(annotation.title!!)"
        var annotationImage = mapView.dequeueReusableAnnotationImage(withIdentifier: reuseIdentifier)
    
        if annotationImage == nil {
            let image = UIImage(named: annotation.title!!)!
            annotationImage = MGLAnnotationImage(image: image, reuseIdentifier: reuseIdentifier)
        }
    
        return annotationImage
    }
    
  4. Adding an MGLSymbolStyleLayer

    let styleLayer = MGLSymbolStyleLayer(layerIdentifier: "bank", sourceIdentifier: "sv")!
    styleLayer.iconImage = "custom-bank" as MGLStyleAttributeValue!
    mapView.style().add(styleLayer)
    

@1ec5
Copy link
Contributor Author

1ec5 commented Sep 27, 2016

You’ve got it backwards: internally, com.mapbox.sprites. is prepended to the reuse identifier to form the sprite name. So com.mapbox.sprites.custom-bank would be the iconImage value.

@rmnblm
Copy link

rmnblm commented Sep 27, 2016

Still no luck when I change it to

styleLayer.iconImage = "com.mapbox.sprites.custom-bank" as MGLStyleAttributeValue!

😞

@1ec5
Copy link
Contributor Author

1ec5 commented Sep 27, 2016

The reuse identifier shouldn’t have that prefix, though. If it doesn’t work even then, you’ll have to wait for the fix outlined here.

@rmnblm
Copy link

rmnblm commented Sep 27, 2016

It doesn't have that prefix. Full implementation details can be found here. You may find a mistake or I may have to wait until this fix is outlined and implemented.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
feature iOS Mapbox Maps SDK for iOS macOS Mapbox Maps SDK for macOS runtime styling
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants