Mapbox Clustering without GeoJSON file - java

how i can clustering markers with this method?
I wanted to make markers group in groups with the current method.
EasyDB stores = EasyDB.init(getActivity(), "Objects");
stores.setTableName("Stores");
Cursor res = stores.getAllData();
while (res.moveToNext()) {
String name = res.getString(5);
String lat = res.getString(8);
String lang = res.getString(9);
String desc = res.getString(4);
mapboxMap.addMarker(new MarkerOptions()
.setIcon(icon)
.position(point)
.setSnippet(snipp)
.title(id));
}

Given the use of MarkerOptions here, it looks like you are using the Mapbox Annotations plugin for Android. If so, you will need to create a GeoJSON object from the markers in your database so that you can use a GeoJsonOptions object to pass to a SymbolManager instance to enable clustering. For example, if you parse your data to a GeoJSON object and store it in a variable geoJsonData:
GeoJsonOptions geoJsonOptions = new GeoJsonOptions()
.withCluster(true)
.withClusterMaxZoom(14)
.withClusterRadius(10);
symbolManager = new SymbolManager(mapView, mapboxMap, style, null, geoJsonOptions);
symbolManager.setIconAllowOverlap(true);
List<SymbolOptions> options = new ArrayList<>();
for (int i = 0; i < geoJsonData.length(); i++) {
Feature feature = features.get(i);
options.add(new SymbolOptions()
.withGeometry((Point) feature.geometry())
.withIconImage("name-of-icon-to-use-for-clusters")
);
}
This example from the Mapbox Android Plugins demo app shows how to implement clustering with annotations.

Related

How to retrieve list of all local audio files and its parameters properly?

For now, I'm doing this with content resolver. Code:
private fun loadMedia(): List<LocalMusic> {
val list = mutableListOf<LocalMusic>()
val contentResolver = context.contentResolver
val uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
val selection = "${MediaStore.Audio.Media.IS_MUSIC}!= 0"
val sortOrder = "${MediaStore.Audio.Media.TITLE} ASC"
var i = 0
contentResolver.query(uri, null, selection, null, sortOrder).use {
if (it != null && it.count > 0) {
while (it.moveToNext()) {
val title = it.getString(it.getColumnIndex(MediaStore.MediaColumns.TITLE))
// Android Studio shows a lint, which tells that MediaStore.MediaColumns.ARTIST
//requires API 30. But in Android Pie it works.
val artist = it.getString(it.getColumnIndex(MediaStore.MediaColumns.ARTIST))
//the same lint as described above(required api 29)
val album = it.getString(it.getColumnIndex(MediaStore.MediaColumns.ALBUM))
// This one is deprecated(fantastic, what to do?).
val source = it.getString(it.getColumnIndex(MediaStore.MediaColumns.DATA))
val duration = it.getLong(it.getColumnIndex(MediaStore.MediaColumns.DURATION))
list.add(LocalMusic(id = "$i", artist = artist, title = title, source = source,
album = album, duration = duration, image = "some uri"))
i++
}
}
}
return list.toList()
}
So, there are some questions:
The main one. Should I use MediaStore.MediaColumns.DATA to get URI anyway? I know that Google tells "sometimes you don't have permissions to work with storage, blah-blah, that's why it's deprecated", but is there a better way(I always have needed permissions)?
About "MediaStore.MediaColumns.ARTIST" and "MediaStore.MediaColumns.ALBUM". Why it requires API 30, if I found its usages in tutorials 5-6 years ago, though there was no API 30 at all? Because of support libraries, migrations, etc? Should I keep using it?

Draw WFS with latlong coordinates to canvas in Java

I am very new to Java programming, I would appreciate any kind of help.
So I want to display a set of lat-long coordinates (more than 50 coordinates) in Java-based canvas (e.g JFrame, Processing) from a WFS server. I have managed to parse lat-long value and print it to the console. Now I'm stuck in how to bring the lat-long coordinates to the screen coordinates (I'd like to draw it on 1000x500 size). I've tried to search for the reference but couldn't find the simplest one for a beginner like me. Here is the current part of my code :
String[] splitc = coord.split(",");
String lon = splitc[0];
String lat = splitc[1];
//parse string to float
float loncoord=Float.parseFloat(lon);
float latcoord=Float.parseFloat(lat);
Can I transfer the coordinates from the WFS to screen coordinates using world2screen.translate of Geotools library as in https://docs.geotools.org/latest/userguide/library/referencing/axis.html ?
In processing, there is a map() function (https://processing.org/reference/map_.html) to transfer from a range to another. I've tried it but it didn't work on my IDE.
One super noob question, I'm trying to store the WFS connection in a function so I can call it in another class, should I store it in static void or use "return"?
If someone can provide an example of a similar task, that would be very helpful. Thanks (Sara)
You can use this formula instead:
float x = ((WIDTH/360.0) * (180 + loncoord));
float y = ((HEIGHT/180.0) * (90 - latcoord));
It should work... Note that it returns a float and takes 5 arguments of the form:
map(input, inputMin, inputMax, outputMin, outputMax)
You only want to create the connection once, so you're left with two viable options: defining the connection as a static variable of a static class, or defining the connection as an instance variable of a class following the singleton pattern.
Assuming you chose the former approach, the method that returns the connection variable should therefore be static but not void:
public static connectionType getConnection() {
return connectionObject;
}
... where connectionType is the datatype of the connection.
The easiest way is to create a GeoTools WFSDataStore, this code builds up the getCapabilities string if the user has given just a URL to the service endpoint and handles authentication if needed. The datastore is stored in a field of the class:
public FetchWFS(String url, String user, String passwd) throws IOException, URISyntaxException {
if (!user.isEmpty()) {
auth = true;
}
baseURL = new URL(url);
List<NameValuePair> nvp = URLEncodedUtils.parse(baseURL.toURI(), "UTF-8");
NameValuePair service = new BasicNameValuePair("service", "wfs");
NameValuePair request = new BasicNameValuePair("request", "getCapabilities");
NameValuePair version = new BasicNameValuePair("version", "2.0.0");
HashMap<String, NameValuePair> parts = new HashMap<>();
parts.put(service.getName(), service);
parts.put(request.getName(), request);
parts.put(version.getName(), version);
for (NameValuePair part : nvp) {
if (part.getName().equalsIgnoreCase("SERVICE")) {
// We don't care what they think this should be
} else if (part.getName().equalsIgnoreCase("REQUEST")) {
// This must be getCapabuilities so we ignore them
} else if (part.getName().equalsIgnoreCase("VERSION")) {
System.out.println("Changing version to " + part.getValue());
parts.put(version.getName(), part);
} else {
parts.put(part.getName(), part);
}
}
URIBuilder builder = new URIBuilder();
builder.setScheme(baseURL.getProtocol());
builder.setHost(baseURL.getHost());
builder.setPort(baseURL.getPort());
builder.setPath(baseURL.getPath());
List<NameValuePair> p = new ArrayList<>();
p.addAll(parts.values());
builder.setParameters(p);
// builder.addParameter("viewparams", "q:\"mySolrQuery\"");
URI uri = builder.build();
System.out.println(uri);
baseURL = uri.toURL();
// fetch the DataStore
Map<String, Object> params = new HashMap<>();
params.put(WFSDataStoreFactory.URL.key, baseURL);
// params.put(WFSDataStoreFactory.WFS_STRATEGY.key, "mapserver");
if (auth) {
params.put(WFSDataStoreFactory.USERNAME.key, user);
params.put(WFSDataStoreFactory.PASSWORD.key, passwd);
}
// params.put(WFSDataStoreFactory.WFS_STRATEGY.key, "mapserver");
datastore = DataStoreFinder.getDataStore(params);
}
Once you have a DataStore (of any type) you can get a list of available
featureTypes and then add one (or more) of them to a map:
JMapFrame frame = new JMapFrame();
MapContent map = new MapContent();
String[] names = datastore.getNames();
featureSource = store.getFeatureSource(names[0]); //fetch first featureType
schema = featureSource.getSchema();
Style style = SLD.createSimpleStyle(schema);
this.layer = new FeatureLayer(featureSource, style);
map.addLayer(layer);
frame.enableToolBar(true);
frame.setMinimumSize(new Dimension(800, 400));
frame.setVisible(true);
The GeoTools Quickstart Tutorial will help you get started with simple mapping, and the Map Styling tutorial will allow you to generate a prettier map when you want more than a simple black and white default map.

Use geojson polygon in geoShapeQuery in ElasticSearch 7.4 java client

Does anyone have a code sample using geojson shape in ElasticSearch 7.x shape query?
It works when I build the shape using ES builders, however I need to use geojson passed in (see below), which likely needs parsing of geojson into ES shape:
{"type":"Polygon","coordinates":[[[-82.30957031249999,26.657277674217585],[-81.7767333984375,25.84686509678058],[-80.90057373046875,24.986058021167594],[-80.25238037109375,25.16517336866393],[-79.97222900390625,26.08885491679362],[-79.771728515625,26.76277822801415],[-80.2606201171875,27.25707120788274],[-80.83740234375,27.332735136859146],[-81.529541015625,27.166695222253114],[-82.30957031249999,26.657277674217585]]]}
I've been struggling with this as well though I actually wanted to feed a JTS Geometry directly to a query. The solution I came up with was to use WrapperQueryBuilder to write the query in JSON:
import org.elasticsearch.common.geo.ShapeRelation;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
...
String queryName = "...";
String geoJson = "...";
QueryBuilder geoShapeQuery = QueryBuilders.wrapperQuery(
String.format(
"{ \"geo_shape\": { \"%s\": { \"shape\": %s, \"relation\": \"%s\" } } }",
queryName,
geoJson,
ShapeRelation.INTERSECTS.getRelationName()));
My query shapes are simple polygons, so I ended up converting it to an array of points and creating a polygon out of these:
if (null != shape) {
JSONObject jsonShape = new JSONObject(shape);
JSONArray coords = jsonShape.getJSONArray("coordinates");
CoordinatesBuilder cb = new CoordinatesBuilder();
for (Object coord: coords.getJSONArray(0)) {
JSONArray c = new JSONArray(coord.toString());
cb.coordinate((Double)c.get(0), (Double)c.get(1));
}
PolygonBuilder pb = new PolygonBuilder(cb);
gsqb = QueryBuilders.geoShapeQuery("FOOTPRINT", pb.buildGeometry());
}

Using ELKI with Mongodb

Using test cases I was able to see how ELKI can be used directly from Java but now I want to read my data from MongoDB and then use ELKI to cluster geographic (long, lat) data.
I can only cluster data from a CSV file using ELKI. Is it possible to connect de.lmu.ifi.dbs.elki.database.Database with MongoDB? I can see from the java debugger that there is a databaseconnection field in de.lmu.ifi.dbs.elki.database.Database.
I query MongoDB creating POJO for each row and now I want to cluster these objects using ELKI.
It is possible to read data from MongoDB and write it in a CSV file then use ELKI to read that CSV file but I would like to know if there is a simpler solution.
---------FINDINGS_1:
From ELKI - Use List<String> of objects to populate the Database I found that I need to implement de.lmu.ifi.dbs.elki.datasource.DatabaseConnection and specifically override the loadData() method which returns an instance of MultiObjectsBundle.
So I think I should wrap a list of POJO with MultiObjectsBundle. Now i'm looking at the MultiObjectsBundle and it looks like the data should be held in columns. Why columns datatype is List> shouldnt it be List? just a list of items you want to cluster?
I'm a little confused. How is ELKI going to know that it should look at the long and lat for POJO? Where do I tell ELKI to do this? Using de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation?
---------FINDINGS_2:
I have tried to use ArrayAdapterDatabaseConnection and I have tried implementing DatabaseConnection. Sorry I need thing in very simple terms for me to understand.
This is my code for clustering:
int minPts=3;
double eps=0.08;
double[][] data1 = {{-0.197574246, 51.49960695}, {-0.084605692, 51.52128377}, {-0.120973687, 51.53005939}, {-0.156876, 51.49313},
{-0.144228881, 51.51811784}, {-0.1680743, 51.53430039}, {-0.170134484,51.52834133}, { -0.096440751, 51.5073853},
{-0.092754157, 51.50597426}, {-0.122502346, 51.52395143}, {-0.136039674, 51.51991453}, {-0.123616824, 51.52994371},
{-0.127854211, 51.51772703}, {-0.125979294, 51.52635795}, {-0.109006325, 51.5216612}, {-0.12221963, 51.51477076}, {-0.131161087, 51.52505093} };
// ArrayAdapterDatabaseConnection dbcon = new ArrayAdapterDatabaseConnection(data1);
DatabaseConnection dbcon = new MyDBConnection();
ListParameterization params = new ListParameterization();
params.addParameter(de.lmu.ifi.dbs.elki.algorithm.clustering.DBSCAN.Parameterizer.MINPTS_ID, minPts);
params.addParameter(de.lmu.ifi.dbs.elki.algorithm.clustering.DBSCAN.Parameterizer.EPSILON_ID, eps);
params.addParameter(DBSCAN.DISTANCE_FUNCTION_ID, EuclideanDistanceFunction.class);
params.addParameter(AbstractDatabase.Parameterizer.DATABASE_CONNECTION_ID, dbcon);
params.addParameter(AbstractDatabase.Parameterizer.INDEX_ID,
RStarTreeFactory.class);
params.addParameter(RStarTreeFactory.Parameterizer.BULK_SPLIT_ID,
SortTileRecursiveBulkSplit.class);
params.addParameter(AbstractPageFileFactory.Parameterizer.PAGE_SIZE_ID, 1000);
Database db = ClassGenericsUtil.parameterizeOrAbort(StaticArrayDatabase.class, params);
db.initialize();
GeneralizedDBSCAN dbscan = ClassGenericsUtil.parameterizeOrAbort(GeneralizedDBSCAN.class, params);
Relation<DoubleVector> rel = db.getRelation(TypeUtil.DOUBLE_VECTOR_FIELD);
Relation<ExternalID> relID = db.getRelation(TypeUtil.EXTERNALID);
DBIDRange ids = (DBIDRange) rel.getDBIDs();
Clustering<Model> result = dbscan.run(db);
int i =0;
for(Cluster<Model> clu : result.getAllClusters()) {
System.out.println("#" + i + ": " + clu.getNameAutomatic());
System.out.println("Size: " + clu.size());
System.out.print("Objects: ");
for(DBIDIter it = clu.getIDs().iter(); it.valid(); it.advance()) {
DoubleVector v = rel.get(it);
ExternalID exID = relID.get(it);
System.out.print("DoubleVec: ["+v+"]");
System.out.print("ExID: ["+exID+"]");
final int offset = ids.getOffset(it);
System.out.print(" " + offset);
}
System.out.println();
++i;
}
The ArrayAdapterDatabaseConnection produces two clusters, I just had to play around with the value of epsilon, when I set epsilon=0.008 dbscan started creating clusters. When i set epsilon=0.04 all the items were in 1 cluster.
I have also tried to implement DatabaseConnection:
#Override
public MultipleObjectsBundle loadData() {
MultipleObjectsBundle bundle = new MultipleObjectsBundle();
List<Station> stations = getStations();
List<DoubleVector> vecs = new ArrayList<DoubleVector>();
List<ExternalID> ids = new ArrayList<ExternalID>();
for (Station s : stations){
String strID = Integer.toString(s.getId());
ExternalID i = new ExternalID(strID);
ids.add(i);
double[] st = {s.getLongitude(), s.getLatitude()};
DoubleVector dv = new DoubleVector(st);
vecs.add(dv);
}
SimpleTypeInformation<DoubleVector> type = new VectorFieldTypeInformation<>(DoubleVector.FACTORY, 2, 2, DoubleVector.FACTORY.getDefaultSerializer());
bundle.appendColumn(type, vecs);
bundle.appendColumn(TypeUtil.EXTERNALID, ids);
return bundle;
}
These long/lat are associated with an ID and I need to link them back to this ID to the values. Is the only way to go that using the ID offset (in the code above)? I have tried to add ExternalID column but I don't know how to retrieve the ExternalID for a particular NumberVector?
Also after seeing Using ELKI's Distance Function I tried to use Elki's longLatDistance but it doesn't work and I could not find any examples to implement it.
The interface for data sources is called DatabaseConnection.
JavaDoc of DatabaseConnection
You can implement a MongoDB-based interface to get the data.
It is not complicated interface, it has a single method.

Android SQLite vs File vs JSON?

I'm using google maps to plot markers on a map. I can save the data for ALL these points (it's over 17000 rows with 3 columns: shopId,shopName,lat,long).
I can also send JSON queries specifying my lat/long and the radius at what shops around I want data about. Then I'll receive the data back. This works, but when I create the markers (with AsyncTask) freezing occurs in the app (and it is noticeable).
This is the code I'm using to generate the custom markers on Google maps:
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
try {
JSONArray jsonArray = new JSONArray(result);
String finalReturn[] = result.split("\\r?\\n");
if(jsonArray.get(0).toString().equals("4")) {
for (int i = 1; i < finalReturn.length; i++) {
jsonArray = new JSONArray(finalReturn[i]);
IconGenerator iconGenerator = new IconGenerator(getApplicationContext());
iconGenerator.setStyle(IconGenerator.STYLE_RED);
iconGenerator.setRotation(90);
iconGenerator.setContentRotation(-90);
Bitmap iconBitmap = iconGenerator.makeIcon(jsonArray.get(5).toString());
Marker marker = mMap.addMarker(new MarkerOptions()
.position(new LatLng(jsonArray.getDouble(6), jsonArray.getDouble(7)))
.icon(BitmapDescriptorFactory.fromBitmap(iconBitmap)));
marker.setTitle(jsonArray.getString(1));
marker.setSnippet(jsonArray.getString(2) + " " + jsonArray.getString(8));
}
}
} catch (JSONException e) {
}
My question is, what is the best solution here, store the points in a MySQL server and generate nearest shops from that area (SQlite Getting nearest locations (with latitude and longitude) something like this), or always query the server for the data. Or maybe a hybrid of both (query the server, then save the data in an SQLite db.)
I'm only a beginner in Android so sorry if this question is simple.
The fastest way should be to save the data in an SQLite db and query it from there, but if you only need the few shops that are near the user, it should be fine to simply call the web service every time.
Other than that, the freezing that occurs in your app is most likely due to the onPostExecute Method being called in the UI-Thread and you doing heavy work in this method.
You should not parse your JSON there, but rather in the doInBackground method and for each parsed element call publishProgress that calls the onProgressUpdate Method (which is also executed in the UI-Thread.
Like this, you can handle setting one single marker on the map at a time and that way, the time between the single onProgressUpdate calls can be used by the system to update the UI and so the freezing should no longer occur.
It should look somewhat like this:
protected Void doInBackground(...) {
String result = getResult();
try {
JSONArray jsonArray = new JSONArray(result);
String finalReturn[] = result.split("\\r?\\n");
if(jsonArray.get(0).toString().equals("4")) {
for (int i = 1; i < finalReturn.length; i++) {
jsonArray = new JSONArray(finalReturn[i]);
publishProgress(jsonArray);
}
}
} catch (JSONException e) {
//handle error
}
}
#Override
protected void onProgressUpdate(JSONArray... progress) {
JSONArray array = progress[0];
IconGenerator iconGenerator = new IconGenerator(getApplicationContext());
iconGenerator.setStyle(IconGenerator.STYLE_RED);
iconGenerator.setRotation(90);
iconGenerator.setContentRotation(-90);
Bitmap iconBitmap = iconGenerator.makeIcon(jsonArray.get(5).toString());
Marker marker = mMap.addMarker(new MarkerOptions()
.position(new LatLng(jsonArray.getDouble(6), jsonArray.getDouble(7)))
.icon(BitmapDescriptorFactory.fromBitmap(iconBitmap)));
marker.setTitle(jsonArray.getString(1));
marker.setSnippet(jsonArray.getString(2) + " " + jsonArray.getString(8));
}

Categories