Add markers to Mapbox from CSV file - android

I have some data points in a csv file located on my website (www.mysite.com/csv_report/myfile.csv) as below:
subject,report_by,Lat,Long
I want to show each of data points as independent markers on Mapbox. I found a solution in Mapbox samples about add single marker but nothing found about multiple markers.
Please help me to find the best way to solve my issue.
Thanks a lot
Zare

Your best joice would probably be to use the by Mapbox provides sources/layers functionality.
The source(s) contain your datapoints, where as the layers you can think of it like an style sheet for HTML. It containes information about how the datapoints are gonna look like.
For creating the layer, you have different options depending on how you want the markers to look like. If you want to show your own icon you have to use a SymbolLayer, but there are many more (CircleLayer for displaying points/circles, ...)
For creating the source use something like the following
style.addLayer(
new SymbolLayer(YOUR_LAYER_ID_STRING, YOUR_SOURCE_ID_STRING)
.withProperties(
// here you can define things like the icon
// icon color
// icon size
// ... many more. Have a look at the Property class
)
);
You can also style your items based on values of your items. Its called data driven styling in Mapbox. See my other answer to maybe get a bit more understanding.
For creating the source use something like the following
ArrayList<Feature> features = new ArrayList<>();
Iterate over your CSV items {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("subject_key", item.subjectValue);
jsonObject.addProperty("report_key", item.reportByValue);
features.add(Feature.fromGeometry(Point.fromLngLat(item.getLat(), item.getLon()), jsonObject);
}
FeatureCollection featureCollection = FeatureCollection.fromFeatures(features);
style.addSource(new GeoJsonSource(YOUR_SOURCE_ID_STRING, featureCollection, new GeoJsonOptions()));
You can furthermore have click listeners to catch clicks on the markers (https://docs.mapbox.com/android/maps/examples/click-on-a-single-layer/).

Related

How to highlight a LineString (or Polygon) in Mapbox Android from a GeoJSON or the mapboxId

I am trying to highlight a feature on a map using Mapbox Android SDK. First, the map is loaded in the app using custom styles from a vector tiles server.
To highlight the feature, the data is received from a server with the coordinates and feature data stored in the database. The android app receives a GeoJSON like this:
{
…
"geometry":{
[enter image description here][1]"type":"LineString",
"coordinates":[
[-6.280378997325897,36.52107960787673],[-6.280381679534912,36.52104619723205],
[-6.280373632907867,36.52102787525949]
]
},
"mapboxId":487
…
}
I use the geometry type to know the type of object and then create a feature of the same type from the geometry itself.
when (current.getGeometryType()) {
POINT -> …
LINE_STRING -> marker.geometry = LineString.fromJson(current.getGeometry())
…
}
Then I add the Source and Layer with some properties.
style.apply {
addSource(
GeoJsonSource(
SOURCE_ID3_RED_BORDER,
FeatureCollection.fromFeatures(marker)
)
)
addLayer(
LineLayer(
LAYER_ID3_RED_BORDER,
SOURCE_ID3_RED_BORDER
)
.withProperties(
PropertyFactory.lineGapWidth(5f),
PropertyFactory.lineJoin(Property.LINE_JOIN_ROUND),
PropertyFactory.lineCap(Property.LINE_CAP_ROUND),
PropertyFactory.lineWidth(2f),
PropertyFactory.lineColor(Color.RED)
)
)
}
But my LineString does not exactly fit with the LineString data received from the server, probably due to generalization of the vector tiles, since we receive
see image
The result is not what I expected, I would like the red line to fit exactly with the green one. I don’t know why the lines don’t fit.
Is there a way to achieve this? It seems to be similar to the "Select Building" example (https://docs.mapbox.com/android/maps/examples/select-a-building/) but the feature to be highlighted is not driven by the selection of the user, but from a know feature data coming from a server when loading the map view.
For some parts of it, like the curve, you do not have enough coordinates to represent the curve. For the straight parts, there appears to be the same problem. When you look at the part below the yellow region, the red lines are perfectly aligned with the green.
In this parts you have sufficient coordinates. In the rest you do not.

How to update existing line layer in MapBox

Here is mapBox sample to draw a line geometry.
private void onStyleLoaded(Style style) {
initRouteCoordinates();
style.addSource(new GeoJsonSource("line-source",
FeatureCollection.fromFeatures(new Feature[]{
Feature.fromGeometry(LineString.fromLngLats(routeCoordinates))})));
style.addLayer(new LineLayer("lineLayer", "line-source")
.withProperties(PropertyFactory.lineDasharray(new Float[] {0.01f, 2f}),
PropertyFactory.lineCap(Property.LINE_CAP_ROUND),
PropertyFactory.lineJoin(Property.LINE_JOIN_ROUND),
PropertyFactory.lineWidth(5f),
PropertyFactory.lineColor(Color.parseColor("#e55e5e"))));
Point point = routeCoordinates.get(0);
mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(
new LatLng(point.latitude(), point.longitude()), 17));
}
I need to add more points and update the line. As you can see in the example line geometry is given to the source layer at the construction time. I couldn't find any api to add a point to the current line later.
Should I remove this line and redraw a new one ? Isn't there a better approach ?
You can add the "line-source" source and corresponding "lineLayer" layer once, and set the data contained by the source each time you want to add more points to update the line. Namely, each time you want to update the data rendered by the LineLayer:
Add the newly acquired coordinate to your existing GeoJSON source, stored in a variable called data. (I'd recommend storing new GeoJsonSource("line-source", ... in a data variable rather than creating it inline within the call to style#addSource, so that you can access it later to update).
Execute style.getSourceAs("line-source").setGeoJson(data).
Alternatively, you could use geoJsonSource.setGeoJson(Feature.fromGeometry(LineString.fromLngLats(updatedRouteCoordinates))) rather than setGeoJson(data), which might be more convenient depending on your use case.
You can modify the implementation according to your specific needs, but the general idea is that you only need to update the existing source's data, and not its corresponding layer, each time you want to update the line. This concept is described in the modify properties documentation for the Mapbox Maps SDK for Android. To quote the documentation:
sources and layers aren't immutable so they can be modified anytime during the map render

How to filter markers in Mapbox Sdk

I'm creating an Android app using MapBox. I've already set up a simple map functionality with markers sourced from .json file. Next step is filtering the markers on the map, just like in this gl-js example here:
https://docs.mapbox.com/mapbox-gl-js/example/filter-markers/
I can't find any sdk examples anywhere, and since this is my first app I really can't figure it out on my own. Any help will be appreciated.
You can check out this example https://docs.mapbox.com/android/maps/examples/multiple-expressions-temperature-change/ that features two layers that fetch a min or max temperature from the data source and display it.
The filtering part is done here:
// Only display Maximum Temperature in this layer
maxTempLayer.setFilter(eq(get("element"), literal("All-Time Maximum Temperature")));
loadedMapStyle.addLayer(maxTempLayer);
Filters accept expressions as arguments, and here Expression.eq is used to compare "element" from the GeoJSON data source (referenced with the Expression.get) with the "All-Time Maximum Temperature" value. If it resolves to true, the feature is going to be displayed, otherwise, it's going to be hidden.

Displaying KML Layers on Maps at Native Android apps Best Practice

I have big KML file to a native Android application, please check the following details and give an advice.
KML file details:
size: 1.7 MB
total number of kml file elements: 500 elements
total number of polygon: 1000 polygon
Android app details:
the above details will be viewed in Fragment
I used the following support library to implement this screen
compile 'com.google.maps.android:android-maps-utils:0.4+'
some caluctations are done on loading the screens(like distance calculations)
Issue:
Take a lot of time to load the map and kml layer about 8 sec
create KMLLayer instance
What is the best practice to implement the above details with good performance?
please advise.
Best practice is doing long time operation in background (for example, on separate thread) and split complex tasks into small parts. So you can:
1) create and start load KML layer as soon as possible (e.g. on app create) and than just show it;
2) instead of one kml file with 500 elements and 1000 polygons, use 50 kml files with 10 elements and 100 polygons and load to layer only necessary files (for example you can split it by area location, or by semantic information, or by something else criteria);
3) combine 1 and 2 points;
4) precisely for google maps it's possible to create tiles with information from kml files and use TileProvider.
Since there's no actual answer, I'll post my own solution.
I've done mainly two things to optimize this.
Instead of using Google maps utils built-in method addLayerToMap - I've used my own custom implementation. I've done so, because parsing kml is relatively fast(in my case - ~5-10 seconds) and it can be done in background thread. Adding all the points to the map, however, takes more than 10 seconds, and must be done on UI thread. Basically, I've used KmlLayer class as a base, and parsed kml file by myself into polygonOptions and all other things I need.
Calculate which items are visible and which are not. Firstly, I filter which items are outside screen bounds. After that, I calculate each item's size(in pixels, no meters) - if item is smaller than threshold - item is also filtered out.
With these hacks applied, instead of freezing app for 15 seconds, user can freely navigate through map, and when he stops, after several seconds information will be displayed.
I just make some updates on the screen behavior to get good performance and good user experience by the following steps:
Divide the KML File to 65 files (the main areas on the map, for example, Zone A11 is located in one KML file and it contains all its details like Zone A11-1, Zone A11-2 and Zone A11-4 ...) and this division is done to be suitable the screen experience (for example user requirements)
on the first load, I am loading only the markers for all KMLs centers and that is not affecting the performance
When user click on the marker, I am loading the KML file for this area and zoom this area
When user zoom out, I am removing this layer from the map and load marker
When user moves the map I am calculating the nearest marker and load its KML layer
Note: preparing files on app launching will not provide better performance because the bad performance comes from adding the KML layer to the google map
Note2: using custom implementation for parsing and adding to Google map take a lot of time or need a lot of unit testing, and I think it's not recommended solution because it's better to leave this solution to be on Google Map utils build-in method (it's customized and always up to date)
I'd suggest you to make sure that you are constructing the KmlLayer on a background thread and addLayerToMap() has to be done on the main thread though like this
GlobalScope.launch {
val stringUrl: String = "https://saltwx.com/bath/SW_bath6.kml"
val inputStream: InputStream = URL(stringUrl).openStream()
val layer = KmlLayer(map,inputStream, applicationContext)
runOnUiThread {
try {
layer.addLayerToMap()
} catch (e: Exception) {
e.printStackTrace()
}
}}

Parse JSON file and display an ImageButton and TextView for each entry

I have a JSON file that I want to parse. For each entry in the JSON file, I want to display an ImageButton (this will be an icon) and a TextView (a title). I'm just wondering what the best way of doing this is..
I DO NOT want to make a listview.. it is more of a grid view. I want 2-3 icons per row (so 2-3 columns) kind of like apple's bookshelf
I've started off with..
json = JSONfunctions.getJSONfromSDCard("/sdcard link to the file") //this gets the json file
JSONArray entries = json.getJSONArray("entries");
ArrayList<String> alTitle = new ArrayList<String>();
for(int i=0;i<entries.length();i++){
JSONObject e = entries.getJSONObject(i);
..//create arraylist to store entries?
//alTitle.add(e.getString("title"));
}
//create ImageButton and TextView?
You can use a listview inside this activity or a ListActivity. Then use a list adapter to create a layout for each list item.
There are samples supplied in the SDK for this to get you started. You will find these in the android-sdk folder wherever you have installed this on your PC.
There are also many tutorials on the internet so I cannont list them all, but here are a few to get you started. These are the top google searches.
Android Series: Custom ListView items and adapters
Android: Dealing with ListActivities, customized ListAdapters and custom-designed items
UPDATE
Sorry I missed the specification of using a GridView. The officaial GridView documentation includes a full sample The same idea applies using an adapter you can compare the different type of view in the Hello Views section of the official documentation
Here is a full example, you will need to add any additional controll into your adapter. Again, there are samples in the sdk itself and these are invaluable to Android developers.
I recommend looking into the GSON library for Android. It's supposedly a lot faster and easier to use than org.json in the stock SDK. I've also ran into bugs with the stock json library.
Basically it adds an interface similar to Java's serializables.
http://code.google.com/p/google-gson/
User Guide: https://sites.google.com/site/gson/gson-user-guide
You can parse the JSON, create an ArrayList then pass that ArrayList to an ArrayAdapter.
Use a custom implementation of ArrayAdapter as mentioned here
android, how to add ListView items from XML?
Create a layout XML with an icon and a TextView and deflate it in the getView method of your ArraAdapter implementation. Populate the values and you are done.

Categories

Resources