If you have CARTO Enterprise account plans, you can connect to the CARTO Engine APIs via Mobile SDK, to retrieve map visualizations and table data from your CARTO account.
There are several methods of connecting map data from your CARTO account (via the Mobile SDK) to your mobile app; depending on the size of your data, the visual requirements, and other factors.
To use a map as raster map tiles, define the tile URL for RasterTileLayer
To apply interactivity (object click data), use UTFGrid. This uses both raster map tiles and json-based UTF tiles. UTFGrids are applicable to both raster and vector tiles, though are more useful for raster tiles. For CARTO Builder Map, you will need to enable and define tooltips with the Pop-up feature
Load vector tiles, the CARTO Engine supports Mapbox Vector Tile (MVT) format tiles, which the Mobile SDK can render on the client side. You will also need CartoCSS styles to view vector tiles. This is useful for applying advanced styling features, such as zooming and rotating maps based on data that can be packaged for offline line, using mbtiles
Load GeoJSON vector data. This is useful if you need need more advanced interactivity (object click actions) or dynamic client-side styling of the objects. For vector data, the CARTO Engine provides a SQL API and mobile app that can load gsimplification and clustering
If the data table is large (more than a ten thousand objects), then loading entire tables can overload the mobile client. Alternatively, use on-demand, view-based loading of vector data. Similar to the SQL API and GeoJSON format used on the CARTO Engine side, the SDK applies custom vector data sources to load data. Only a selected, visible area, of the map will load. The mobile app can control zoom levels, server-side generalizations, and simplifications can be applied
For point-geometry time-series visualizations, use the Animated aggregation to define Torque maps. This provides animated rendering, and the Mobile SDK has a special layer TorqueTileLayer to define this.
Offline maps
CARTO Mobile SDK can take map offline via the CARTO platform:
Upload your data to CARTO, create a new map with CARTO Builder, and apply map custom styling with CartoCSS.
Load the package file to the mobile device - you can have your app download it from your server, or add it as bundled asset to your app.
Add the map to MapView, as a VectorTileLayer, from MBTilesTileDataSource and CartoCSS.
This method enables you to get both optimized vector tiles and suitable CartoCSS styling for your map.
For details, see the Offline Maps page.
Online maps
CARTO Mobile SDK supports CARTO Maps API integration for Anonymous Maps and Named Maps. Anonymous maps allow you to instantiate a map given SQL and CartoCSS. Named Maps are essentially the same as Anonymous Maps except the MapConfig is stored on the server, and the map is given a unique name.
Anonymous Map
Use CartoMapsService class to configure layers. Note that this must be done in a separate thread on Android, as Maps API requires connecting to server, which is not allowed in the main thread. The following snippet sets up the config (SQL and CartoCSS) we are going to use to define the map. As Anonymous map means that the map configuration - data and styles is created by client, then there are two phases.
privateStringgetConfigJson(){// Define server configJSONObjectconfigJson=newJSONObject();try{// Change these according to your DBStringsql="SELECT * FROM stations_1";StringstatTag="3c6f224a-c6ad-11e5-b17e-0e98b61680bf";String[]columns=newString[]{"name","status","slot"};StringcartoCSS="#stations_1{"+"marker-fill-opacity:0.9;marker-line-color:#FFF;"+"marker-line-width:2;marker-line-opacity:1;marker-placement:point;"+"marker-type:ellipse;marker-width:10;marker-allow-overlap:true;}\n"+""+"#stations_1[status = 'In Service']{marker-fill:#0F3B82;}\n"+"#stations_1[status = 'Not In Service']{marker-fill:#aaaaaa;}\n"+"#stations_1[field_9 = 200]{marker-width:80.0;}\n"+"#stations_1[field_9 <= 49]{marker-width:25.0;}\n"+"#stations_1[field_9 <= 38]{marker-width:22.8;}\n"+"#stations_1[field_9 <= 34]{marker-width:20.6;}\n"+"#stations_1[field_9 <= 29]{marker-width:18.3;}\n"+"#stations_1[field_9 <= 25]{marker-width:16.1;}\n"+"#stations_1[field_9 <= 20.5]{marker-width:13.9;}\n"+"#stations_1[field_9 <= 16]{marker-width:11.7;}\n"+"#stations_1[field_9 <= 12]{marker-width:9.4;}\n"+"#stations_1[field_9 <= 8]{marker-width:7.2;}\n"+"#stations_1[field_9 <= 4]{marker-width:5.0;}";// You may not need to change much of these standardsconfigJson.put("version","1.0.1");configJson.put("stat_tag",statTag);JSONArraylayersArrayJson=newJSONArray();JSONObjectlayersJson=newJSONObject();layersJson.put("type","cartodb");JSONObjectoptionsJson=newJSONObject();optionsJson.put("sql",sql);optionsJson.put("cartocss",cartoCSS);optionsJson.put("cartocss_version","2.1.1");JSONArrayinteractivityJson=newJSONArray();interactivityJson.put("cartodb_id");optionsJson.put("interactivity",interactivityJson);JSONObjectattributesJson=newJSONObject();attributesJson.put("id","cartodb_id");JSONArraycolumnsJson=newJSONArray();for(Stringcol:columns){columnsJson.put(col);}attributesJson.put("columns",columnsJson);optionsJson.put("attributes",attributesJson);layersJson.put("options",optionsJson);layersArrayJson.put(layersJson);configJson.put("layers",layersArrayJson);}catch(JSONExceptione){returnnull;}returnconfigJson.toString();}
fungetConfigJson():String?{// Define server configvalconfigJson=JSONObject()try{// Change these according to your DBvalsql="SELECT * FROM stations_1"valstatTag="3c6f224a-c6ad-11e5-b17e-0e98b61680bf"valcolumns=arrayOf("name","status","slot")valcartoCSS="#stations_1{"+"marker-fill-opacity:0.9;marker-line-color:#FFF;"+"marker-line-width:2;marker-line-opacity:1;marker-placement:point;"+"marker-type:ellipse;marker-width:10;marker-allow-overlap:true;}\n"+""+"#stations_1[status = 'In Service']{marker-fill:#0F3B82;}\n"+"#stations_1[status = 'Not In Service']{marker-fill:#aaaaaa;}\n"+"#stations_1[field_9 = 200]{marker-width:80.0;}\n"+"#stations_1[field_9 <= 49]{marker-width:25.0;}\n"+"#stations_1[field_9 <= 38]{marker-width:22.8;}\n"+"#stations_1[field_9 <= 34]{marker-width:20.6;}\n"+"#stations_1[field_9 <= 29]{marker-width:18.3;}\n"+"#stations_1[field_9 <= 25]{marker-width:16.1;}\n"+"#stations_1[field_9 <= 20.5]{marker-width:13.9;}\n"+"#stations_1[field_9 <= 16]{marker-width:11.7;}\n"+"#stations_1[field_9 <= 12]{marker-width:9.4;}\n"+"#stations_1[field_9 <= 8]{marker-width:7.2;}\n"+"#stations_1[field_9 <= 4]{marker-width:5.0;}";// You may not need to change much of these standardsconfigJson.put("version","1.0.1")configJson.put("stat_tag",statTag)vallayersArrayJson=JSONArray()vallayersJson=JSONObject()layersJson.put("type","cartodb")valoptionsJson=JSONObject()optionsJson.put("sql",sql)optionsJson.put("cartocss",cartoCSS)optionsJson.put("cartocss_version","2.1.1")valinteractivityJson=JSONArray()interactivityJson.put("cartodb_id")optionsJson.put("interactivity",interactivityJson)valattributesJson=JSONObject()attributesJson.put("id","cartodb_id")valcolumnsJson=JSONArray()for(iin0..columns.size-1){columnsJson.put(columns[i])}attributesJson.put("columns",columnsJson)optionsJson.put("attributes",attributesJson)layersJson.put("options",optionsJson)layersArrayJson.put(layersJson)configJson.put("layers",layersArrayJson)}catch(e:JSONException){returnnull}returnconfigJson.toString()}
2) Now that the config is set up, you initiate the map and create Layer to be added to the MapView. This snippet inits vector tiles, but you can change the default vector layer mode to false if you want to get a raster tiles.
finalStringconfig=getConfigJson();// Use the Maps service to configure layers.// Note that this must be done in a separate thread on Android,// as Maps API requires connecting to server, which is not allowed in main thread.ThreadserviceThread=newThread(newRunnable(){@Overridepublicvoidrun(){CartoMapsServicemapsService=newCartoMapsService();mapsService.setUsername("nutiteq");mapsService.setDefaultVectorLayerMode(true);// use vector layerstry{LayerVectorlayers=mapsService.buildMap(Variant.fromString(config));mapView.getLayers().addAll(layers);}catch(IOExceptione){Log.e("EXCEPTION","Exception: "+e);}}});serviceThread.start();
JsonValueconfig=JsonUtils.VectorLayerConfigJson;MapView.ConfigureAnonymousVectorLayers(config);// Extension method in static classpublicstaticvoidConfigureAnonymousVectorLayers(thisMapViewmap,JsonValueconfig){// Use the Maps service to configure layers. // Note that this must be done in a separate thread on Android, // as Maps API requires connecting to server, which cannot be done in the main thread.System.Threading.Tasks.Task.Run(delegate{CartoMapsServiceservice=newCartoMapsService();service.Username="nutiteq";service.DefaultVectorLayerMode=true;// Use VectorLayersLayerVectorlayers=service.BuildMap(Variant.FromString(config.ToString()));map.Layers.AddAll(layers);});}
1
2
3
4
5
6
7
8
9
10
11
12
13
NTCartoMapsService*mapsService=[[NTCartoMapsServicealloc]init];[mapsServicesetUsername:@"nutiteq"];// Use vector layers, not raster layers[mapsServicesetDefaultVectorLayerMode:TRUE];[mapsServicesetInteractive:TRUE];NTVariant*variant=[NTVariantfromString:[selfgetConfig]];NTLayerVector*layers=[mapsServicebuildMap:variant];[[self.mapViewgetLayers]addAll:layers];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
letconfig=getConfig()// This calculation should be in background threadDispatchQueue.global(qos:.userInitiated).async{letmapsService=NTCartoMapsService()mapsService?.setUsername("nutiteq")mapsService?.setDefaultVectorLayerMode(true)// use vector layersletlayers=mapsService?.buildMap(NTVariant.fromString(config))self.mapView?.getLayers()?.addAll(layers)}
valconfig=getConfigJson()// Use the Maps service to configure layers.// Note that this must be done in a separate thread on Android,// as Maps API requires connecting to server, which is not allowed in main thread.// Remember: Put your operations back on the main thread to change the UI// Note:// doAsync requires anko coroutines dependency// compile "org.jetbrains.anko:anko-sdk25-coroutines:$anko_version"doAsync{valmapsService=CartoMapsService()mapsService.username="nutiteq"mapsService.isDefaultVectorLayerMode=true// use vector layerstry{vallayers=mapsService.buildMap(Variant.fromString(config))mapView?.layers?.addAll(layers)}catch(e:IOException){println("Exception: "+e.message)}}
Named Map
If you have created Named map using CARTO Maps API then map is already configured in the server, and map initiation is simpler:
finalCartoMapsServiceservice=newCartoMapsService();// Use vector layersservice.setDefaultVectorLayerMode(true);service.setUsername("nutiteq");finalStringname="tpl_69f3eebe_33b6_11e6_8634_0e5db1731f59";// Be sure to make network queries on another threadThreadthread=newThread(newRunnable(){@Overridepublicvoidrun(){try{LayerVectorlayers=service.buildNamedMap(name,newStringVariantMap());mapView.getLayers().addAll(layers);}catch(IOExceptione){e.printStackTrace();}}});thread.start();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
MapView.ConfigureNamedVectorLayers("tpl_69f3eebe_33b6_11e6_8634_0e5db1731f59");// Extension methodpublicstaticvoidConfigureNamedVectorLayers(thisMapViewmap,stringname){System.Threading.Tasks.Task.Run(delegate{CartoMapsServiceservice=newCartoMapsService();service.Username="nutiteq";// Use VectorLayersservice.DefaultVectorLayerMode=true;LayerVectorlayers=service.BuildNamedMap(name,newStringVariantMap());map.Layers.AddAll(layers);});}
1
2
3
4
5
6
7
8
9
10
11
NTCartoMapsService*mapsService=[[NTCartoMapsServicealloc]init];[mapsServicesetUsername:@"nutiteq"];// Use vector layers[mapsServicesetDefaultVectorLayerMode:YES];NTLayerVector*layers=[mapsServicebuildNamedMap:@"tpl_69f3eebe_33b6_11e6_8634_0e5db1731f59"templateParams:[[NTStringVariantMapalloc]init]];[[self.mapViewgetLayers]addAll:layers];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
letservice=NTCartoMapsService()// Use vector layersservice?.setDefaultVectorLayerMode(true)service?.setUsername("nutiteq")letname="tpl_69f3eebe_33b6_11e6_8634_0e5db1731f59"DispatchQueue.global(qos:.userInitiated).async{letlayers=service?.buildNamedMap(name,templateParams:NTStringVariantMap())self.mapView?.getLayers()?.addAll(layers)}
valservice=CartoMapsService()// Use vector layersservice.isDefaultVectorLayerMode=trueservice.username="nutiteq"valname="tpl_69f3eebe_33b6_11e6_8634_0e5db1731f59"// Be sure to make network queries on another thread// Remember: Put your operations back on the main thread to change the UI// Note:// doAsync requires anko coroutines dependency// compile "org.jetbrains.anko:anko-sdk25-coroutines:$anko_version"doAsync{try{vallayers=service.buildNamedMap(name,StringVariantMap())mapView?.layers?.addAll(layers)}catch(e:IOException){e.printStackTrace()}}
SQL API
CARTO’s SQL API allows you to interact with your tables and data inside CARTO, as if you were running SQL statements against a normal database. In general you can use the SQL API to insert, update or delete data (i.e., insert a new column with a latitude and longitude data) or to select data from public tables in order to use it on your website or application (i.e., display the 10 closest records to a particular location).
Note: In mobile SDK you can only SELECT data from public tables without api_key for higher security. Private tables and using api_key is not allowed from mobile directly. If you need these, you need to proxy CARTO SQL API to a custom API towards your app, so api_key requests are done from server to server
staticfinalStringquery="SELECT * FROM cities15000 WHERE population > 100000";finalCartoSQLServiceservice=newCartoSQLService();service.setUsername("nutiteq");// Be sure to make network queries on another threadThreadthread=newThread(newRunnable(){@Overridepublicvoidrun(){try{features=service.queryFeatures(query,baseProjection);for(inti=0;i<features.getFeatureCount();i++){// This data set features point geometry,// however, it can also be LineGeometry or PolygonGeometryPointGeometrygeometry=(PointGeometry)features.getFeature(i).getGeometry();source.add(newPoint(geometry,getPointStyle()));}}catch(IOExceptione){e.printStackTrace();}}});thread.start();
conststringquery="SELECT * FROM cities15000 WHERE population > 100000";CartoSQLServiceservice=newCartoSQLService();service.Username="nutiteq";PointStyleBuilderbuilder=newPointStyleBuilder{Color=newCarto.Graphics.Color(255,0,0,255),Size=1};MapView.QueryFeatures(service,source,builder.BuildStyle(),query);// Extension Method in a static class (shared code)publicstaticvoidQueryFeatures(thisMapViewmap,CartoSQLServiceservice,LocalVectorDataSourcesource,PointStylestyle,stringquery){System.Threading.Tasks.Task.Run(delegate{FeatureCollectionfeatures=service.QueryFeatures(query,map.Options.BaseProjection);for(inti=0;i<features.FeatureCount;i++){Featurefeature=features.GetFeature(i);// This data set features point geometry,// however, it can also be LineGeometry or PolygonGeometryPointGeometrygeometry=(PointGeometry)feature.Geometry;varpoint=newPoint(geometry,style);source.Add(point);}});}
// Only get cities with over 100k, or else it will be too many resultsNSString*sql=@"SELECT * FROM cities15000 WHERE population > 100000";// Initialize CartoSQL service, set a usernameNTCartoSQLService*service=[[NTCartoSQLServicealloc]init];[servicesetUsername:@"nutiteq"];NTPointStyleBuilder*builder=[[NTPointStyleBuilderalloc]init];NTColor*color=[[NTColoralloc]initWithR:255g:0b:0a:255];[buildersetColor:color];[buildersetSize:1];NTPointStyle*style=[builderbuildStyle];dispatch_queue_tqueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0ul);// Set on background thread for "animated" point appeardispatch_async(queue,^{NTFeatureCollection*features=[servicequeryFeatures:sqlproj:self.projection];for(inti=0;i<[featuresgetFeatureCount];i++){// This data set features point geometry,// however, it can also be LineGeometry or PolygonGeometryNTPointGeometry*geometry=(NTPointGeometry*)[[featuresgetFeature:i]getGeometry];NTPoint*point=[[NTPointalloc]initWithGeometry:geometrystyle:style];[self.sourceadd:point];}});
letquery="SELECT * FROM cities15000 WHERE population > 100000"letservice=NTCartoSQLService()service?.setUsername("nutiteq")letbuilder=NTPointStyleBuilder()builder?.setSize(1.0)builder?.setColor(NTColor(r:255,g:0,b:0,a:255))letstyle=builder?.buildStyle()letsource=NTLocalVectorDataSource(projection:projection);// Networking should be placed on a background thread// Remember: Put your operations back on the main thread to change the UIDispatchQueue.global(qos:.userInitiated).async{letfeatures=service?.queryFeatures(query,proj:projection)letcount=Int((features?.getFeatureCount())!)foriin0..<count{// This data set features point geometry,// however, it can also be LineGeometry or PolygonGeometryletgeometry=features?.getFeature(Int32(i)).getGeometry()as!NTPointGeometrysource?.add(NTPoint(geometry:geometry,style:style))}}
valquery="SELECT * FROM cities15000 WHERE population > 100000"valservice=CartoSQLService()service.username="nutiteq"valbuilder=PointStyleBuilder()builder.size=1Fbuilder.color=Color(255,0,0,255)valstyle=builder.buildStyle()// Remember: Put your operations back on the main thread to change the UI// Note:// doAsync requires anko coroutines dependency// compile "org.jetbrains.anko:anko-sdk25-coroutines:$anko_version"doAsync{try{valfeatures=service.queryFeatures(query,projection)for(iin0..features.featureCount-1){// This data set features point geometry,// however, it can also be LineGeometry or PolygonGeometryvalgeometry=features.getFeature(i).geometryasPointGeometrysource?.add(Point(geometry,style))}}catch(e:IOException){}}