I am an android beginner and can't find the answer anywhere.
I want to show the markers on the map that are 25 km from the center of the map.
I can't find the way to find the location of the center of the map, then show the markers that are 25km from it.
The geopoints are on Parse.com in the Cars.class, in the row "location".
So far, event the code to show the markers doesn't work, it gets the results:
{"objectId": "[...]","location": {"__type":"GeoPoint","latitude": [...],"longitude": [...] },"createdAt":"[...]","updatedAt": "[...]"}
It crashes and gives me the error:
com.parse.ParseObject cannot be cast to com.company.test.Cars.
Thanks for the help!
ParseQuery<Cars> query = ParseQuery.getQuery("Cars");
query.selectKeys(Collections.singletonList("location"));
query.findInBackground(new FindCallback<Cars>(){
#Override
public void done(List<Cars> list, ParseException e) {
if (e != null) {
return;
}
ParseGeoPoint geoPoint;
for(int i =0;i<list.size();i++){
geoPoint = list.get(i).getParseGeoPoint("location");
mMap.addMarker(new MarkerOptions().position(new LatLng(geoPoint.getLatitude(), geoPoint.getLongitude())));
Ok so there is two options to achieve what you try to do.
One is a bit complicated but very powerful: everything is described here
Btw after you apply this option, it will be:
ParseQuery<Cars> query = ParseQuery.getQuery(Cars.class);
The other would sound less complex to me:
Create a Parse.Query<Parse.Object> to get Parse.Object instead of Cars.
In the done method, iterate through all the result
Create a constructor of Car which takes a Parse.Object as parameter
Convert every Parse.Object from the results into a Car Object via the constructor you just created.
This second option is maybe a bit more complex but way more flexible.
Related
I am working with Google Maps. Let's say I have around 1 million position markers stored in my database. What I want to do is load or display only those markers that should be on the part of map that is currently shown on screen. (For example, if my map is showing Asia, it should only show markers that are in Asia; if I move to any other place on the map, it should show markers in that region.) I'm doing this so I don't have to load the entire database at once, since that can cause delays in the app. I tried using Spatialite, but I didn't find a good tutorial, or information on how to use it. This is one of the links I followed, but I didn't get a good idea. Is there any other way to do this, or is Spatialite the best option?
You will have to figure out the best way to retrieve these positions from your database, but one way to add markers based on the map's camera's position is to add relevant markers in GoogleMap.OnCameraChangeListener.
// Check to see if your GoogleMap variable exists.
if (mGoogleMap != null) {
// Respond to camera movements.
mGoogleMap.setOnCameraMoveListener(new GoogleMap.OnCameraMoveListener() {
#Override
public void onCameraMove() {
// Get the current bounds of the map's visible region.
LatLngBounds bounds = mGoogleMap.getProjection().getVisibleRegion().latLngBounds;
// Loop through your list of positions.
for (LatLng latLng: yourLatLngList) {
// If a position is inside of the bounds,
if (bounds.contains(latLng)) {
// Add the marker.
mGoogleMap.addMarker(new MarkerOptions()
.position(latLng));
}
}
}
});
}
I wouldn't suggest looping through a million positions every time the map's camera's position is changed. I think the best way for you to do it is to get the current bounds of the map's visible region when the camera is moved, then send the bounds in a call to your backend on a different thread, and have your backend do the work of finding the positions that fit in those bounds and then return that list back to your app where you can add markers accordingly.
I have a simple app that show a lot of markers on a map (Google Map). At the moment I have over 3,000 markers and this number keeps growing as the database gets bigger.
I also have a few checkboxes that dictate which markers are to be shown, vice versa.
Originally I was adding each marker like this (inside a loop going through every location)
Marker marker;
marker = mMap.addMarker (markerOptions);
mMarkerArrayList.add(marker);
Then when the user interacted with the checkboxes, I simply made markers visible/invisible. This is faster removing, and re-adding the markers.
The code in the Checkbox Change Listener was something like this:
for (Marker marker : mMarkerArrayList) {
if (condition) {
marker.setVisible(false);
}
}
And then make the visible again on another change of the checkbox.
However, adding all these markers individually in the beginning was taking a long time (1-2 seconds) and as the markers can only be added in the UI thread, it was freezing the UI (including my progress bar) for that amount of time.
After a lot of research, and not getting anywhere, the only thing I could do was add the markers using the ClusterManager (android-maps-utils library). This loads twice as fast, and does not block my UI.
Adding code is something like this (here myLocation is the object of my MyLocation class which holds the lcoation and relevant data for each point).
for (MyLocation myLocation : mMyLocationArrayList) {
mClusterManager.addItem(myLocation)
}
Now the issue is linking the checkboxes to these ClusterItem objects. I cannot find any method, or field in the library to change the visibility of these markers (ClusterItem). In the previous method (changing the visibility) was almost instantaneous (fast enough that the user would not feel any lag). However, now, I need to add and remove these markers everytime, and there is a lag, and it is very obvious to the user.
Does anyone have any suggestions?
You should be able to use mClusterManager.getMarkerCollection().getMarkers() in order to get the Marker collection, and then hide certain Markers in the same way as you were before:
Collection<Marker> markerCollection = mClusterManager.getMarkerCollection().getMarkers();
for (Marker marker : markerCollection) {
if (condition) {
marker.setVisible(false);
}
}
#Khash I have had some success with the following code (i.e. hides both the Markers and Clusters), although I experience a similar issue when I zoom the map in or out the Markers and Clusters re-appear.
Hope this helps, happy coding!
if (hide) {
/* Hide all Markers and Clusters */
mMyClusterManager.getMarkerCollection().hideAll();
mMyClusterManager.getClusterMarkerCollection().hideAll();
} else {
/* Show all Markers and Clusters */
mMyClusterManager.getMarkerCollection().showAll();
mMyClusterManager.getClusterMarkerCollection().showAll();
}
I have an android map with google maps that dynamically loads markers from the server based on user screen position. So for example if user moves the map I simply made request to the server and I'm sending screen bounds and based on that I get markers data (id and corrdinates) which is later parsed and created to actual pointers. Problem is that when user goes back to the same area (which markers was previously created) I still making same request and get same data (but obviously I won't let to recreate that marker so I just run for loop throught all markers that there are on map and check if map marker id is equal server send data marker id and if it is equal I simply break the loop)
try {
Collection<MarkerItemData> mapMarkers = algorithm.getItems();
JSONObject jsonObject = new JSONObject(strings[0]);
JSONArray respondArray = jsonObject.getJSONArray("respond");
list = new ArrayList();
for (int i = 0; i < respondArray.length(); i++) {
JSONObject station = respondArray.getJSONObject(i);
int id = station.getInt("_id");
boolean skip = false;
for (final MarkerItemData m : mapMarkers) {
if (m.getId() == id) {
skip = true;
break;
}
}
}
}
However I don't think that this approach is the best. I also have other ideas that should work (at least I think)
send to server screen bounds and also all marker id's that are visible on the screen (I could select all markers that is in screen range and which ids are not in screen bounds)
each time delete markers from android app and basically recreate all the markers from server each time (Personally I think this is a bad solution)
So which of those ideas are the best? Any other ideas? (Sorry for my english)
Sending screen bounds to the server and id-s of currently visible markers on screen is your best bet. But still there are a couple of troublesome issues. How will you find all markers contained within the range you specified with screen bounds? What if new markers come to your server or some markers are deleted? Can you come with a maintainable structure in such a situation or will you test each point that corresponds a marker in the database one by one to see whether it locates in the range or not? Considering all these, you need to find a way to optimize storing and querying points, in other words latitude and longitude pairs that point markers. You should perform spatial indexing by using one of the common spatial index methods.
There are lots of methods for spatial indexing and one might be slightly better than the other one depending on use case. To make long story short, since you need to query range in this scenario, you should implement a quadtree. The quadtree is called as a tree data structure in which each internal node has exactly four children (northwest, northeast, southwest, southeast). If you have no knowledge about this structure, I believe you can understand its basics in an hour but explaining it in detail from scratch will cause loss of too much time for me. Therefore, I am skipping implementation details about quadtrees. There are several sources which already explained this structure much better than I could, and you can easily find an open source library.
I will only give pseudo-ish Java method for your quadtree to find all points that appear within the range of screen bounds excluding the ones that are already in the previous screen:
ArrayList<LatLng> queryRange(QuadLevel level, float[] screenBounds, ArrayList<LatLng> prevPoints) {
// Initialize a list to hold the found points
ArrayList<LatLng> pointsInRange = new ArrayList<>();
if (!quadtreeBounds.intersects(screenBounds))
return pointsInRange;
// Find the points that are in the current quad level
for (LatLng point : level.getPoints()) {
// If the current point is in screen bounds and it is not contained by prevPoints
if (point.isInRange(screenBounds)
&& !prevPoints.contains(point))
pointsInRange.add(point);
}
// If there are no children, return
if (level.hasNoChildren())
return pointsInRange;
// Else, continue to look up children
pointsInRange.addAll(queryRange(level.northwest, screenBounds, prevPoints));
pointsInRange.addAll(queryRange(level.northeast, screenBounds, prevPoints));
pointsInRange.addAll(queryRange(level.southwest, screenBounds, prevPoints));
pointsInRange.addAll(queryRange(level.southeast, screenBounds, prevPoints));
return pointsInRange;
}
Store all the Visible Markers on Screen in List or Local Db or Some place And Iterate over the Existing Markers in list if Not Present Request From Server
Extend HashSet<MarkerOptions>, so that when a MarkerOptions is added you add it to your map.
class MarkerSet extends HashSet<MarkerOptions> {
#Override
public boolean add(MarkerOptions markerOptions) {
boolean didAdd = super.add(markerOptions);
if(didAdd) {
mMap.addMarker(markerOptions);
}
return didAdd;
}
}
Maintain a LatLngBounds object in your maps activity. This LatLngBounds object will store bounds for which you already have requested data from the server.
When map's camera moves check if new bounds are within currently stored bounds if not request the server and increase bounds else do not request the server.
mMap.setOnCameraMoveListener(new GoogleMap.OnCameraMoveListener() {
#Override
public void onCameraMove() {
LatLngBounds currentBounds = mMap.getProjection().getVisibleRegion().latLngBounds;
if (mBounds == null) {
mBounds = currentBounds;
// Perform server request and get markers passing in currentBounds
// Following to be performed in server request's callback
MarkerOptions markerOptions = new MarkerOptions();
// set marker options here
mMarkerSet.add(markerOptions);
} else {
if (!(mBounds.contains(currentBounds.northeast) || mBounds.contains(currentBounds.southwest))) {
mBounds = mBounds.including(currentBounds.northeast);
mBounds = mBounds.including(currentBounds.southwest);
// Perform server request and get markers passing in currentBounds
// Following to be performed in server request's callback
MarkerOptions markerOptions = new MarkerOptions();
// set marker options here
mMarkerSet.add(markerOptions);
}
}
}
});
Ensure that you are always invoking server request for currentBounds. This algorithm can be improved if you manage to shorten currentBounds when currentBounds and mBounds are overlapping.
Assumption
Please note that assumption here is that when the camera moves, currentBounds will fall slightly out of mBounds. Otherwise, new markers are not loaded.
Referring to the image, if the user moves from visible green region to orange the mBounds value will be the blue rectangle, and therefore white area will not be covered. However, in practice, it is not easy to move camera perfectly horizontally or vertically, this will cause invocation of server method and new markers for the unventured area to be loaded.
Hope this helps. :)
I have a problem with using Google maps API on Android. I have a button which removes the selected marker off the maps interface, and information about it from the SQLite db that I have set up. Although my only problem is once multiple markers are on the map this feature stops and doesn't remove them:
Below shows the method which removes the marker from the maps and replaces them. As I said this works perfectly fine with a single marker and my testing has been a success, but with multiple it does not work.
I have an onclicklisterner for the markers which displays the information and a popup box for the marker, for the user to remove the marker they must click the marker which sets the global variable to that object then once the remove button is pressed the removeMarker() method is called. The getAllMarkers() method loops through a SQLite db and pulls information and adds to the maps.
End problem: Removing a marker when multiple markers are placed on the map doesn't work. Only works when a single marker is placed on the map.
Marker lastOpened = null;
To remove the information from the db the condition in the if statement returns a boolean value if it has been done:
if(this.mDbHelper.deleteLine(lastOpened.getTitle()))
Remove method
public void removeMarker(){
if(this.lastOpened != null){
if(this.mDbHelper.deleteLine(lastOpened.getTitle())){
lastOpened.remove();
getAllMarkers();
}
}
}
Thanks
You can either use googleMap.clear() or you can store your Markers in a collection of kind and remove them in a loop:
private ArrayList<Marker> mMarkers;
...
private void removeMarkers() {
for (Marker marker: mMarkers) {
marker.remove();
}
mMarkers.clear();
}
Here's a related ticket discuss how to remove marker: Remove a marker from a GoogleMap
I'm trying to implement app in which you can add your own marker which will be given a shutdown time. To do so I need to know how to manage my markers. Let's say I have array list of users with assigned unique ID.
private ArrayList<User> userList = new ArrayList<>();
Then I will create array list of markers which will contain information like Latitude, Longitude, Title, owner's ID and deadline.
private ArrayList<MyMarker> mMarkersArray = new ArrayList<MyMarker>();
Next whenever user will activate add marker method, new marker will be pushed to my list of markers. Ideologically everything seems nice and easy, furthermore creating new object looks like this:
Marker mMarker = mMap.addMarker(new MarkerOptions() (...) );
but when it comes to managing specific markers it seems like I'm missing something. There will be some trigger method which will check the deadline of all markers (or rather first one on the 'sorted by deadline' list) and then it should remove (not hide, because I think it would be inefficient from the memory point of view). How to achieve this? I can't add some custom variable like ID to markers (so I could then find the one I'm interested in) and I'm a bit lost.
There is a way to achieve this by clearing whole map and then rendering again all markers except the inactive, but as far as I'm concerned it's very inefficient and there has to be better solution.
If you want to remove a specific marker from the map, you can just call the remove(). method.
Sample code to remove a marker from MapView and HashMap:
for (Marker marker : hm.keySet()) {
if (hm.get(marker).equals(deadline)) {
marker.remove();
hm.remove(marker);
}
}
You dont need to clear the entire MapView, if you just call remove() method on specific marker.