I am using the HERE Maps Lite SDK for Android as a library in my project.
I want to show MapView, and add overlays of all shelters I have in my database, in their specific coordinates.
The map works well, but the shown coordinates are not accurate. I tried to geocode the coordinates in lat-long website, and they are correct, but in the map they are shown right to their real location.
My code:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_shelters_map);
mapView = findViewById(R.id.mapview);
mapView.onCreate(savedInstanceState);
ActivityCompat.requestPermissions(SheltersMapActivity.this, new String[] {
Manifest.permission.ACCESS_FINE_LOCATION}, 123);
if (ContextCompat.checkSelfPermission(this, ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
// Permission is not granted
Toast.makeText(getApplicationContext(), "אנא אפשר גישה לשירותי מיקום", Toast.LENGTH_SHORT).show();
ActivityCompat.requestPermissions(this,
new String[]{ACCESS_FINE_LOCATION},
1);
} else // premission is granted
{
GPStracker g = new GPStracker(getApplicationContext());
userLocation = g.getLocation();
}
loadMapScene();
addSheltersOverlay();
// loadMapScene();
}
private void loadMapScene() {
// Load a scene from the SDK to render the map with a map style.
mapView.getMapScene().loadScene(MapStyle.NORMAL_DAY, new MapScene.LoadSceneCallback() {
#Override
public void onLoadScene(#Nullable MapScene.ErrorCode errorCode) {
if (errorCode == null) {
mapView.getCamera().setTarget(new GeoCoordinates(userLocation.getLatitude(),
userLocation.getLongitude()));
mapView.getCamera().setZoomLevel(15);
} else {
Log.d("data1", "onLoadScene failed: " + errorCode.toString());
}
}
});
}
private void addSheltersOverlay() {
db = new DatabaseHandler(this);
ArrayList<Shelter> places = this.db.getAllPlaces();
Shelter userLocationPlace = new Shelter("המיקום שלך", "", userLocation, null, 0, "");
places.add(userLocationPlace);
int size = places.size();
for(int i = 0; i < size; i++) {
TextView textView = new TextView(this);
textView.setTextColor(Color.parseColor("#FFFFFF"));
textView.setText(places.get(i).getName());
LinearLayout linearLayout = new LinearLayout(this);
if (places.get(i) instanceof Basement)
linearLayout.setBackgroundColor(Color.BLACK);
else if (places.get(i) instanceof Stairs)
linearLayout.setBackgroundColor(Color.GREEN);
else
linearLayout.setBackgroundColor(Color.BLUE);
linearLayout.setPadding(10, 10, 10, 10);
linearLayout.addView(textView);
GeoCoordinates geoCoordinates = new GeoCoordinates(places.get(i).getLocation().getLatitude(),
places.get(i).getLocation().getLongitude());
MapOverlay<LinearLayout> mapOverlay = new MapOverlay<>(linearLayout, geoCoordinates);
mapView.addMapOverlay(mapOverlay);
}
}
The shown map:
.
I can see the streets names in the shown map itself, and I see that it is not the accurate point.
Anybody help?
From the code it looks correct, but I cannot see the location vs. the expected location. By default, MapOverlays are drawn centered on the coordinates. You can set an anchor point to move the overlay in relation to the coordinates. This could help if there is always an offset between the coordinates in your database and the location on the map.
Maybe, can you try to render a small MapCircle item onto the map at the expected location? Then you can more easily see where that location lies on the map. You can compare the results with https://www.latlong.net/.
Related
As stated in the title, essentially I only want to display the GroundOverlay when the camera is in view of the entire building. How would I accomplish this within the onCameraMove() method? As of now the overlay appears even when part of the building is within the camera view.
#Override
public void onCameraMove(){
LatLngBounds bounds = mMap.getProjection().getVisibleRegion().latLngBounds;
private static final LatLng Building1 = new LatLng(54.69726685890506,-2.7379201682812226);
if(mMap.getCameraPosition().zoom > 17){
if (bounds.contains(Building1)) {
displayOverlay();
}
}
It seems that Building1 should be a list of the boundary points of the building, and you should check in a loop that ALL of them are contained inbounds:
...
List<LatLng> buildingPoints = new ArrayList<>();
buildingPoints.add(new LatLng(...,...))
buildingPoints.add(new LatLng(...,...))
...
...
if(mMap.getCameraPosition().zoom > 17){
boolean allPointsVisible = true;
for (LatLng currBuildingPoint: buildingPoints) {
if (!bounds.contains(currBuildingPoint)) {
allPointsVisible = false;
break;
}
}
if (allPointsVisible) {
displayOverlay();
}
}
...
I know there are similar questions around, but I was not able to find a solid answer, here is my question: Is there any way I can make Markers without a Google Map reference to be stored in an ArrayList (or any other storage), and then just simply add them to my map?
Background:
I have an app, that at the moment has around 3,500 markers. Each marker also has a data associated with it (boolean array storing data for each marker which is used to make them visible/invisible based on users interactions). At the moment, I get these marker's location and data using a class that extends AsyncTask. After the loading is finished, then I create my markers using this data on the main Thread. However, this takes some time, and it freezes the UI while the markers are being created and added to the map. I want to do this somehow in the background.
What I have tried so far:
I created another class that extends AsyncTask passing in my LocationData and my Google Map object. But I get an error when I try to make the markers in my Async class. I get a Runtime error saying I need to do this on the UI thread.
java.lang.RuntimeException: An error occurred while executing
doInBackground()
Caused by: com.google.maps.api.android.lib6.common.apiexception.c: Not
on the main thread
I have also thought about just making MarkerOptions object in the background and then use that to create markers in the main thread; however, I cannot add a tag to the MarkerOption, it needs to be added to the marker. In that case, I need to go through all of them again in the main thread just to add the tag, which I feel like is not saving me too much time/resources.
Any suggestion/help would be appreciated on how to create these markers and attach their tags without blocking the UI?
Thanks in advance.
Here are some of my code:
LocationLoader class
(BinLocation is my Location class, each object has boolean variables (marker tags) and LatLng)
public class LocationLoader extends AsyncTaskLoader> {
private String TAG = LocationLoader.class.getName();
String[] fileNameArray;
//ArrayLists
private ArrayList<BinLocation> mBinLocationArrayList = new ArrayList<>();
public LocationLoader(Context context, String... fileNames){
super(context);
//get the file names that was passed in
fileNameArray = fileNames;
}//LocationLoader
#Override
protected void onStartLoading() {
Log.v(TAG, "onStartLoading called");
forceLoad();
}//onStartLoading
#Override
public ArrayList<BinLocation> loadInBackground() {
Log.v(TAG, "loadInBackground called");
String path = "/storage/emulated/0/";
File file;
String output = "";
//Read data from file
for (int i = 0; i < fileNameArray.length; i++) {
file = new File(path + fileNameArray[i]);
try (Scanner scanner = new Scanner(file)) {
//first line of the text, containing the location and version
output = scanner.nextLine();
String prefix = (output.split(":"))[0];
String line;
while (scanner.hasNextLine()) {
line = scanner.nextLine();
String inputArray[] = line.split(",");
BinLocation binLocation = new BinLocation(
prefix + "-" + inputArray[0],
Double.parseDouble(inputArray[1]),
Double.parseDouble(inputArray[2]),
Integer.parseInt(inputArray[3].trim()),
Integer.parseInt(inputArray[4].trim()),
Integer.parseInt(inputArray[5].trim()),
Integer.parseInt(inputArray[6].trim()));
mBinLocationArrayList.add(binLocation);
}//while
} catch (Exception e) {
Log.e(TAG, "File read error: ", e);
}
}//for
Log.v(TAG, "readLocation finished");
Log.v(TAG, "ArrayList size: " + mBinLocationArrayList.size());
return mBinLocationArrayList;
}//loadInBackground
}//LocationLoader class
Here is my MarkerLoader class (I have tried this and got the doInBackground() error). Also there is no code right now here for adding the data to the marker but it foes in the loop right after it has been added to the map.
public class MarkerLoader extends AsyncTaskLoader<ArrayList<Marker>> {
private GoogleMap map;
private ArrayList<Marker> mMarkerArrayList = new ArrayList<>();
private ArrayList<MyLocation> mBinLocationArrayList = new ArrayList<>();
public MarkerLoader (Context context, GoogleMap map, ArrayList<BinLocation> binLocationArrayList) {
super(context);
this.map = map;
this.mBinLocationArrayList = binLocationArrayList;
}//MarkerLoader
#Override
protected void onStartLoading() {
Log.v(TAG, "onStartLoading called");
forceLoad();
}//onStartLoading
#Override
public ArrayList<Marker> loadInBackground() {
Log.v(TAG, "loadInBackground called");
Marker marker;
for (BinLocation binLocation : mMyLocationArrayList) {
marker = map.addMarker(new MarkerOptions()
.position(binLocation.getPosition()));
mMarkerArrayList.add(marker);
}
Log.v(TAG, "loadInBackground finished, with: " + mMarkerArrayList.size());
return mMarkerArrayList;
}
}
This is the helper function(populateMap()) in the main Activity that makes the markers and save them in an ArrayList
private void populateMap() {
if (!checkMapReady() || !mMapIsEmpty) {
return;
}//if Map Not ready
//Initialize ArrayList
mMarkerArrayList = new ArrayList<>();
/**
* This part uses the loop to go through each MyLocation object in the ArrayList, extract
* all the data, and set the markers
*/
//Check to make sure the BinLocation ArrayList is not empty otherwise we will crash
if (mBinLocationArrayList.isEmpty()) {
Log.w(TAG, "populateMap() terminated, mBinLocationArrayList empty");
return;
}//if BinLocation empty
//Safety check to clear the map before populating it
mMap.clear();
//create a markerMyLocation object
Marker mMaker;
//This goes through the ArrayList for every MyLocation object and sets up the markerMyLocation
for (BinLocation binLocation : mBinLocationArrayList) {
//get boolean values
boolean[] booleanValues = {binLocation.getGarbage(), binLocation.getContainer(),
binLocation.getPaper(), binLocation.getCompost()};
//Set different icon
switch (markerIconPreference) {
case "customIcon":
//custom icon
//Decide what icon to use
if (booleanValues[0] && !booleanValues[1] && !booleanValues[2] && !booleanValues[3]) {
//Make a new MarkerOptions object to add the data
//garbage markers
mMaker = mMap.addMarker(new MarkerOptions()
.title(binLocation.getId())
.position(binLocation.getPosition())
.icon(BitmapDescriptorFactory.fromResource(R.drawable.marker_garbage))
.visible(garbageVisible));
} else {
//Make a new MarkerOptions object to add the data
//recycling markers
mMaker = mMap.addMarker(new MarkerOptions()
.title(binLocation.getId())
.position(binLocation.getPosition())
.icon(BitmapDescriptorFactory.fromResource(R.drawable.marker_recycling))
.visible(recyclingVisible));
}
//Add our boolean array as an object to our markerMyLocation
mMaker.setTag(booleanValues);
//Add the markerMyLocation to the ArrayList
mMarkerArrayList.add(mMaker);
break;
case "coloredTeardrop":
//teardrop icon
//Decide what icon to use
if (booleanValues[0] && !booleanValues[1] && !booleanValues[2] && !booleanValues[3]) {
//Make a new MarkerOptions object to add the data
//garbage markers
mMaker = mMap.addMarker(new MarkerOptions()
.title(binLocation.getId())
.position(binLocation.getPosition())
.visible(garbageVisible));
} else {
//Make a new MarkerOptions object to add the data
//recycling markers
mMaker = mMap.addMarker(new MarkerOptions()
.title(binLocation.getId())
.position(binLocation.getPosition())
.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN))
.visible(recyclingVisible));
}
//Add our boolean array as an object to our markerMyLocation
mMaker.setTag(booleanValues);
//Add the markerMyLocation to the ArrayList
mMarkerArrayList.add(mMaker);
break;
}//switch
}//for
//disable the progress bar
mProgressBar.setVisibility(View.GONE);
//De-activate the CountDown timer since the map is ready
mCountDownTimer.cancel();
//set the boolean to false
mMapIsEmpty = false;
Log.v(TAG, "populateMap finished. Markers: " + mMarkerArrayList.size());
}//populateMap
Here is the onMapReady function
public void onMapReady(GoogleMap map) {
Log.v(TAG, "onMapReady called");
mMap = map;
//Setup on map loaded
mMap.setOnMapLoadedCallback(this);
//Check to see if the map is empty and the location array list is not empty and then call populateMap
if (mMapIsEmpty && !mBinLocationArrayList.isEmpty()) {
populateMap();
}//if map empty
//set bounds
mMap.setLatLngBoundsForCameraTarget(GREATER_VANCOUVER_BOUND);
//Set min zoom level to match the bound
mMap.setMinZoomPreference(10.0f);
//disable map toolbar
UiSettings mUiSettings = mMap.getUiSettings();
mUiSettings.setMapToolbarEnabled(false);
//Set listeners
mMap.setOnMarkerClickListener(this);
mMap.setOnInfoWindowCloseListener(this);
mMap.setOnInfoWindowClickListener(this);
// Setting our custom info window, passing out helper method
mMap.setInfoWindowAdapter(new CustomInfoWindowAdapter());
//Here we check for permission and setup the map accordingly
if (!checkLocationPermission()) {
//Permission is not granted, log, and use the default location
Log.v(TAG, "No location permission");
//setup default map
defaultMapSetup();
} else {
Log.v(TAG, "Location permission granted");
//Enable my location and initialize the map there
mMap.setMyLocationEnabled(true);
//Setup the map
locationMapSetup();
}//if -permission
}//onMapReady
try to Create your markers in another thread like this
YourActivity.this.runOnUiThread(new Runnable(){
public void run(){
//paste your code here.
});
I'm using osmdroid and have implemented a MapEventsReceiver, MapEventsOverlay and a LongPressHelper in order to add a new marker when the user holds down on the map. This works the first time and the first time only.
If I remove the addMarker(p); from my longPressHelper then it will fire every single time.
Has anyone got any idea why this is happening like this?
code:
mapviewInit - called in onCreate
private void mapviewInit() {
mapview = (MapView) findViewById(R.id.mapview);
mapview.setTileSource(TileSourceFactory.MAPNIK);
mapview.setBuiltInZoomControls(true);
mapview.setMultiTouchControls(true);
IMapController mapController = mapview.getController();
mapController.setZoom(16);
GeoPoint startPoint = new GeoPoint(48.8583, 2.2944);
mapController.setCenter(startPoint);
MapEventsReceiver meReceiver = new MapEventsReceiver() {
#Override
public boolean singleTapConfirmedHelper(GeoPoint p) {
return false;
}
#Override
public boolean longPressHelper(GeoPoint p) {
Toast toast = Toast.makeText(getApplicationContext(), "DEBUGDEBUGDEBUG", Toast.LENGTH_LONG);
toast.show();
addMarker(p);
return true;
}
};
addMarker - called by LongPressHelper
public void addMarker(GeoPoint geoPoint) {
Drawable dr = getResources().getDrawable(R.drawable.icn_crosshair_red);
Bitmap bitmap = ((BitmapDrawable) dr).getBitmap();
Drawable d = new BitmapDrawable(getResources(), Bitmap.createScaledBitmap(bitmap, 100, 100, true));
mapview.getOverlays().clear();
mapview.invalidate();
selectedPosMarker = new Marker(mapview);
selectedPosMarker.setPosition(geoPoint);
selectedPosMarker.setInfoWindow(null);
selectedPosMarker.setIcon(d);
selectedPosMarker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM);
mapview.getOverlays().add(selectedPosMarker);
mapview.invalidate();
}
I assume you add a MapEventsOverlay somewhere, in a part of your code you didn't provided.
The issue is that you remove it in addMarker! :
mapview.getOverlays().clear();
So of course it is not present to react to long press on the second time...
Don't call mapview.invalidate() twice in the same method, it's useless and time-consuming.
I need to implement few gestures in activity. I used Genymotion for that, saved gestures file in res/raw folder and wrote a code which is showing all good but keeps crashing the application. Does anyone know what could be the possible reason? I really tried to solve, but it seems i am missing something!
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GestureOverlayView gesturesView = new GestureOverlayView(this);
View inflate = getLayoutInflater().inflate(R.layout.activity_garden,
null);
gesturesView.addView(inflate);
gesturesView.addOnGesturePerformedListener(this);
gestures = GestureLibraries.fromRawResource(this, R.raw.gestures);
if (!gestures.load()) {
finish();
}
setContentView(gesturesView);
}
public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) {
ArrayList<Prediction> predictions = gestures.recognize(gesture);
int index = 0;
double maxScore = predictions.get(index).score;
for (int i = 1; i < predictions.size(); i++) {
if (predictions.get(i).score > maxScore) {
index = i;
maxScore = predictions.get(i).score;
}
}
Prediction p = predictions.get(index);
if (p.name.equalsIgnoreCase("Love"))
daisy.setImageResource(sp.getInt("Love", 0));
if (p.name.equalsIgnoreCase("Hit"))
daisy.setImageResource(sp.getInt("Hit", 0));
if (p.name.equalsIgnoreCase("Pet"))
daisy.setImageResource(sp.getInt("Pet", 0));
Toast.makeText(this, p.name + "\n" + p.score, Toast.LENGTH_SHORT)
.show();
}
On your code, what happen if you don't get any prediction?
Well, index will be equals to 0, and predictions.get(index) will crash because there is no object at index 0.
public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) {
ArrayList<Prediction> predictions = gestures.recognize(gesture);
if (predictions == null || predictions.isEmpty()) {
return;
}
// continue the regular flow
}
I'm trying to display a few points (points of interest) with custom InfoWindow in google maps android. My problem is that I cannot put different information in the different points. I'm facing problems updating the textview content of the popup layout. See my sample code below.
My infoWindowAdapter code:
public class poisInfoWindowAdapter implements InfoWindowAdapter {
#Override
public View getInfoWindow(Marker arg) {
return null;
}
#Override
public View getInfoContents(Marker marker) {
//Get Layout of POI's popups and assign values to text views.
View InfoPopupLayout = getLayoutInflater().inflate(R.layout.infopopup, null);
TextView t = ((TextView)InfoPopupLayout.findViewById(R.id.title));
t.setText(name);
return InfoPopupLayout;
}
}
The code responsable for adding Points to map:
public void onPostExecute(String responsePois) {
try {
JSONArray P = new JSONArray(responsePois);
for (int i = 0; i < P.length(); i++) {
JSONObject pois = P.getJSONObject(i);
position = new LatLng(pois.getDouble("y"), pois.getDouble("x"));
name = pois.getString("name_pt");
Map.setInfoWindowAdapter(new poisInfoWindowAdapter());
Map.addMarker(new MarkerOptions()
.position(position)
.title(name)
);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
In this case all of my points get the same name. The infoWindowAdapter class can't get the correct values from name variable. Anyone knows how to resolve this problem?
I think that what I say and how I explain is understandable but if not, tell me and I will answer.
Thanks
First of all put your Map.setInfoWindowAdapter(new poisInfoWindowAdapter()); outside your for loop in onPostExecute(....)
and second implement your poisInfoWindowAdapter() like below:
public class poisInfoWindowAdapter implements InfoWindowAdapter {
#Override
public View getInfoWindow(Marker arg) {
return null;
}
#Override
public View getInfoContents(Marker marker) {
//Get Layout of POI's popups and assign values to text views.
View InfoPopupLayout = getLayoutInflater().inflate(R.layout.infopopup, null);
TextView t = (TextView)InfoPopupLayout.findViewById(R.id.title);
t.setText(marker.getTitle());
TextView t2 = (TextView)InfoPopupLayout.findViewById(R.id.title2);
t2.setText(marker.getSnippet());
return InfoPopupLayout;
}
}
Update: set Marker as
Currnt = mMap.addMarker(new MarkerOptions()
.icon(BitmapDescriptorFactory.fromResource(R.drawable.mark_red))
.position(new LatLng(latitude,longitude)
.title(locations)
.snippet(city));