Search code examples
androidioscartodbcarto-mobile

How to make transparent map background with CARTO mobile SDK


I want to show a different View below map (e.g. some animation), and need to make map partially transparent for this. How can I do this with CARTO Mobile SDK?


Solution

  • Ok, thanks for asking, this actually wasn't the most trivial task.

    Details depend what parts of map you want to make transparent. Let's assume you want to sea areas transparent. Essentially, you have four different levels that need to be transparent then:

    1. Basemap's water layer
    2. Basemap's land color (technically map background color)
    3. GLView's background color
    4. MapView's background bitmap

    My example snippets are in Swift, but should be easily convertible to other languages.

    In Carto's Mobile SDK, the land is actually solid background color covering whole map, water is placed on top of it as blue (or dark)-colored polygons. So if you wish to make the water layer transparent, you also need to make the map background transparent, but this creates also transparent land. To avoid this you need to 'color' land side with some polygons; normal vector tiles do not have land coverage, but we have created special tileset in a mapbox account, what you can use as VectorTileLayer:

        // Purple land for visibility. You probably want to keep it white
        let css = "#land-polygons { polygon-fill: #826DBA; polygon-opacity: 1.0;}"
        // Load the land layer (polygons) from a tileset in MapBox
        let url = "https://a.tiles.mapbox.com/v4/jaakl.landpolygons-mz12/{z}/{x}/{y}.vector.pbf?access_token=YOUR_MAPBOX_TOKEN"
    
        let baseSource = NTHTTPTileDataSource(minZoom: 0, maxZoom: 12, baseURL: url)
    
        let baseDecoder = NTMBVectorTileDecoder(cartoCSSStyleSet: NTCartoCSSStyleSet(cartoCSS: css))
        let baseLayer = NTVectorTileLayer(dataSource: baseSource, decoder: baseDecoder)
    
        contentView!.mapView?.getLayers().add(baseLayer)
    

    Now that you've got your land area colored, you would add another layer with real basemap. For this we need a customized basemap style - with transparent water/sea - and bundle it with your app. Carto's Voyager style is available here.

    Unzip it, and make the following changes in the following files:

    hydro.mss:

    @water: transparent;// desaturate(#A0DBE6,26%);
    @water_shadow: transparent; // rgb(210,234,237);
    

    style.mss:

    Map {
      background-color: transparent;
    

    Compress the files (make sure there is no folder in zip, else the SDK won't find any files) and add the new style to your assets folder (if Android) or anywhere (if iOS).

    Now you can load your style from an asset file like this:

        let baseDecoder = NTMBVectorTileDecoder(cartoCSSStyleSet: NTCartoCSSStyleSet(cartoCSS: css))
        let baseLayer = NTVectorTileLayer(dataSource: baseSource, decoder: baseDecoder)
        contentView!.mapView?.getLayers().add(baseLayer)
    
        let data = NTAssetUtils.loadAsset("cartostyles-custom.zip")
        let package = NTZippedAssetPackage(zip: data)
        let styleSet = NTCompiledStyleSet(assetPackage: package)
    
        let source = NTCartoOnlineTileDataSource(source: "carto.streets")
        let decoder = NTMBVectorTileDecoder(compiledStyleSet: styleSet)
    
        let layer = NTVectorTileLayer(dataSource: source, decoder: decoder)
        contentView!.mapView!.getLayers().add(layer)
    

    Then set your GlView's background transparent, and set mapview background bitmap to null, and you're ready to go:

        // Set clear background for our GLView
        mapView?.backgroundColor = UIColor.clear
    
        // Ensure that there's no background bitmap
        mapView?.getOptions().setBackgroundBitmap(nil)
    

    When you're developing Android, you also need to add the following two lines for the transparent NTMapView background to properly render, else it'll stay black:

    mapView.setZOrderOnTop(true);
    mapView.getHolder().setFormat(PixelFormat.RGBA_8888);
    

    Here I've set the parent views background to red. Now I see the following when the MapView renders:

    enter image description here