Some background:
I have a map where I allow the user to click and select a place. Then I am getting the address from the place where they clicked.
What I want to do: Using that address, which is a String, I want to get details about that place by using the new Google Map Places API.
I'm attempting to do this with the code below
//Make the Autocomplete request builder with the String address
FindAutocompletePredictionsRequest.Builder requestBuilder =
FindAutocompletePredictionsRequest.builder()
.setQuery(address)
.setTypeFilter(TypeFilter.ADDRESS);
placesClient = Places.createClient(getApplicationContext());
//Create the Autocomplete Task
Task<FindAutocompletePredictionsResponse> task =
placesClient.findAutocompletePredictions(requestBuilder.build());
task.addOnSuccessListener(new OnSuccessListener<FindAutocompletePredictionsResponse>() {
#Override
public void onSuccess(final FindAutocompletePredictionsResponse findAutocompletePredictionsResponse) {
//Use the response to get a list of Predictions
List<AutocompletePrediction> list = findAutocompletePredictionsResponse.getAutocompletePredictions();
if (list.isEmpty() || list == null) {
} else {
//Get the first Autocomplete prediction and later, it's ID.
AutocompletePrediction ap = list.get(0);
//Provide a list of Place Fields that I want to retrieve.
List<Place.Field> placeFields = Arrays.asList(Place.Field.ID, Place.Field.NAME, Place.Field.ADDRESS);
//Make the request to fetch the place using the Place ID obtained from the Autocomplete prediction.
FetchPlaceRequest request = FetchPlaceRequest.builder(ap.getPlaceId(), placeFields).build();
placesClient = Places.createClient(getApplicationContext());
//Using the Place ID from the Autocomplete prediction, make a request to fetch places.
placesClient.fetchPlace(request).addOnSuccessListener(new OnSuccessListener<FetchPlaceResponse>() {
#Override
public void onSuccess(FetchPlaceResponse fetchPlaceResponse) {
//Use the response to get the Place Object
Place place = fetchPlaceResponse.getPlace();
//Display the results from the Place Object
displayResults("\n-----------------\nName: " + place.getName() + "\nAddress: " + place.getAddress());
}
}).addOnFailureListener(new OnFailureListener() {
//......
});
}
}
});
task.addOnFailureListener(new OnFailureListener() {
//......
});
Problem: The problem is that when I do this and use place.getName(), instead of displaying the location's name, as it should, it displays the first part of the address like the street number and name. Additionally, other Fields like the phone number and opening hours are null.
Does anybody know how I can get the Place Object from the String address?
Getting details about the place where the user clicks is the most important feature of my Android app.
I am using the new Places API since the Place Details and Place Picker are now deprecated.
Related
I am making a weather app where I use a weather API and Volley to get the JsonObject with a request, then parse the values and display the values in textViews in another activity(screen).
I am now calling this method below in my MainActivity and using Intent to send the values to my displayInfo activity.
public void getInfoMethod(){
String finalUrl ="";
String cityName = searchBar.getText().toString().trim();
RequestQueue rQ = Volley.newRequestQueue(getApplicationContext());
//create a requestQueue to add our request into
finalUrl = leftApiUrl+cityName+rightApiUrl;
StringRequest sR = new StringRequest(Request.Method.POST, finalUrl, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
result = "";
try {
JSONObject allJsonRes = new JSONObject(response);
String name = allJsonRes.getString("name");
double visibility = allJsonRes.getDouble("visibility");
int timeZone =allJsonRes.getInt("timezone");
//Creates a new JSONArray with values from the JSON string.
//try/catch are mandatory when creating JSONObject
//now we extract values from this JsonObject
JSONArray weatherJsonArr = allJsonRes.getJSONArray("weather");
//store []weather
//1.to get mainDescription and subDescription
//store the []weather part into weatherJsonArr
//inside this JsonArray,we store the only JsonObject as weatherBlock
//{}0
//then get different values from this subJsonObject
JSONObject weatherBlock = weatherJsonArr.getJSONObject(0);
//this includes id,main,description,icon
String mainDescription = weatherBlock.getString("main");
//get the string under key "main" e.g. "rain"
String subDescription = weatherBlock.getString("description");
//e.g."moderate rain"
JSONObject mainBlock = allJsonRes.getJSONObject("main");
//access {}main
double temp_in_C = mainBlock.getDouble("temp");
//get temperature from {}main
double temp_feel = mainBlock.getDouble("feels_like");
double temp_min = mainBlock.getDouble("temp_min");
double temp_max = mainBlock.getDouble("temp_max");
double pressure = mainBlock.getDouble("pressure");
double humidity = mainBlock.getDouble("humidity");
JSONObject windBlock = allJsonRes.getJSONObject("wind");
//get wind{}
double windSpeed = windBlock.getDouble("speed");
double degree = windBlock.getDouble("deg");
///
JSONObject sysBlock = allJsonRes.getJSONObject("sys");
String country = sysBlock.getString("country");
///
result += "Current weather in "+ name+", "+country+": "
+"\ntime zone: "+ timeZone
+"\nvisibility: "+ visibility
+"\nTemperature: "+Math.round(temp_in_C)+"°C"
+"\n"+mainDescription
+"\n("+subDescription+")"
+"\nWind speed : "+ windSpeed+" meters per minute"
+"\ndegree: "+degree
+"\ntemp feel:"+Math.round(temp_feel)+"°C"
+"\nmin: "+Math.round(temp_min)+"°C/"+"max"+Math.round(temp_max)+"°C"
+"\npressure: "+pressure
+"\nhumidity: "+humidity;
//then send these values to the displayInfo activity
//using Intent and putExtra
Intent i =new Intent(MainActivity.this,displayInfo.class);
i.putExtra("city",name);
i.putExtra("mainD",mainDescription);
i.putExtra("subD",subDescription);
i.putExtra("temp",temp_in_C);
i.putExtra("tempMax",temp_max);
i.putExtra("tempMin",temp_min);
i.putExtra("tempFeel",temp_feel);
i.putExtra("pressure",pressure);
i.putExtra("humidity",humidity);
i.putExtra("visibility",visibility);
i.putExtra("speed",windSpeed);
i.putExtra("deg",degree);
i.putExtra("timezone",timeZone);
startActivity(i);
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener(){
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(getApplicationContext(),"Error,check network or spelling",Toast.LENGTH_SHORT).show();
}//note that .show() is necessary for the message to show
});
rQ.add(sR);
//add the request into the queue,Volley will handle it and send it
//and then onResponse() or onErrorResponse() will run
//https://developer.android.com/training/volley/simple
}
It works fine by now, but the problem is, now I want to implement the observer pattern, get the JsonObject in my MainActivity(subject) and make the observers(displayInfo.class for now) to get the latest JsonObject from subject, so I need a method that could return the JSONObject in the MainAvtivity, what should I do to implement this method for observer pattern?
(not using inbuilt Observer interface)
Firstly I suggest putting your getInfoMethod() in a helper class. This will allow for easier re-usability.
Next, I wouldn't gather your result in your first activity. Instead, I would build the URL like you are. Then create an Intent to your second activity and pass the URL as a string with i.putExtra(finalUrl.toString).
In your second activity, have a loading spinner visible, that gets set to 'gone' at the end of processing your result. If an error occurs you can always call finish() to send you back to your first activity.
Optionally you could create a POJO for the results and use Jackson to map the results to an object. It'll be easier to pass the one object around instead of working with every little bit of a JSONObject. JSONObjects are fine, but once you have the data the way you want it, you should map it to a class if you are expecting to work with the object for any length of time.
I have an application in which I pass conferenceDto object with User Ids to my conferenceService where it needs to be added to a Conference Model. The problem is that the conferenceDto list of user ids is a string (ex. "2,4"). I am trying to find the best way of turning this collection of strings to a list of objects of type User
My conferenceService method:
#Override
public Conference updateConference(#Valid ConferenceDto conferenceDto){
Authentication user1 = SecurityContextHolder.getContext().getAuthentication();
User user = userService.findByUsername(user1.getName());
Optional<Conference> conferenceTemp = findById(conferenceDto.getConference_id());
if (nameExist(conferenceDto.getName()) && !conferenceDto.getName().equals(conferenceTemp.get().getName())) {
throw new ConferenceAlreadyExistException(
"There is a conference with that name: "
+ conferenceDto.getName());
}
Conference conference = new Conference();
conference.setConference_id(conferenceDto.getConference_id());
conference.setCreator(user);
conference.setName(conferenceDto.getName());
conference.setDescription(conferenceDto.getDescription());
conference.setStartConference(conferenceDto.getStartConference());
conference.setEndConference(conferenceDto.getEndConference());
conference.setStudents(Collections.singletonList(userService.findById(conferenceDto.getStudents()))); // doesnt work this way because findById requires type long but here I am using Collection<Strings>
return conferenceRepository.save(conference);
}
I am quite new to Java and Spring so Im not sure if this needs a for loop to fill a new list and then pass it to conference.setStudents or it can be done another way. Any tips is very appreciated!
p.s. Type Conference's students is a Collection<User>
I found a solution. I did create a for loop and it turns the collection of strings into a collection of users like I want to. Although, it does not save to my attendance_table for some reason
Changed conferenceService method to:
#Override
public Conference updateConference(#Valid ConferenceDto conferenceDto){
Authentication user1 = SecurityContextHolder.getContext().getAuthentication();
User user = userService.findByUsername(user1.getName());
Optional<Conference> conferenceTemp = findById(conferenceDto.getConference_id());
if (nameExist(conferenceDto.getName()) && !conferenceDto.getName().equals(conferenceTemp.get().getName())) {
throw new ConferenceAlreadyExistException(
"There is a conference with that name: "
+ conferenceDto.getName());
}
Conference conference = new Conference();
conference.setConference_id(conferenceDto.getConference_id());
conference.setCreator(user);
conference.setName(conferenceDto.getName());
conference.setDescription(conferenceDto.getDescription());
conference.setStartConference(conferenceDto.getStartConference());
conference.setEndConference(conferenceDto.getEndConference());
Collection<User> userCollection = new ArrayList<>();
for (String s: conferenceDto.getStudents()){
System.out.println(s);
userCollection.add(userService.findById((long) Integer.parseInt(s)).get());
}
conference.setStudents(userCollection);
return conferenceRepository.save(conference);
}
private boolean nameExist(String name) {
return conferenceRepository.findByName(name) != null;
}
Google warns us about PlaceID changes and suggests;
Place IDs may change due to large-scale updates on the Google Maps database. In such cases, a place may receive a new place ID, and the old ID returns a NOT_FOUND response.
You can refresh Place IDs free of charge, by making a Place Details request, specifying only the ID field in the fields parameter.
Unfortunately, there is no sample code for Java/Kotlin on their website except a link to their web services
String placeID ="Some place ID";
List<Place.Field> placeFields = Arrays.asList(
Place.Field.ID,
);
// Construct a request object, passing the place ID and fields array.
FetchPlaceRequest request = FetchPlaceRequest.builder(placeID, placeFields).build();
placesClient.fetchPlace(request).addOnSuccessListener((response) -> {
Place place = response.getPlace();
if (!placeID.equals(place.getId())) {
//update your old placeID
}
}).addOnFailureListener((exception) -> {
if (exception instanceof ApiException) {
//Place removed.
}
});
Is this a porper way to update a placeIDs with Java?
I've tested my databases and found a case where the update occurs. The below code contains both a renewed placeID, removed placeID, and still valid palceID so that one can test.
ArrayList<String> placeIDsList = new ArrayList<>();
placeIDsList.add("ChIJTaKjynxesBQREvi1CU5QUFg");
placeIDsList.add("EjhDdW1odXJpeWV0IE1haGFsbGVzaSwgVXp1biBTay4sIEV6aW5lL8OHYW5ha2thbGUsIFR1cmtleSIuKiwKFAoSCakQkmN8XrAUEVkLpNK_F4IJEhQKEgmFzKyYe16wFBGSjU7F2ooIIg");
placeIDsList.add("hIJy9YVxdxpsBQRq0-xUVJdZQ8");
// Specify the fields to return (in this example all fields are returned).
List<Place.Field> placeFields = Arrays.asList(Place.Field.ID);
for (String plc : placeIDsList ) {
// Construct a request object, passing the place ID and fields array.
FetchPlaceRequest request = FetchPlaceRequest.builder(plc, placeFields).build();
Log.e(TAG,"request for place with ID = " + plc);
placesClient.fetchPlace(request).addOnSuccessListener((response) -> {
Place place = response.getPlace();
if (!plc.equals(place.getId())) {
Log.e(TAG,"placeID renewed");
Log.e(TAG,"placeID old = " + plc);
Log.e(TAG,"placeID new = " + place.getId());
} else {
Log.e(TAG, "Place found: " + place.getId());
}
}).addOnFailureListener((exception) -> {
if (exception instanceof ApiException) {
ApiException apiException = (ApiException) exception;
int statusCode = apiException.getStatusCode();
// Handle error with given status code.
Log.e(TAG, "Place with ID "+plc+" not found");
Log.e(TAG, "Exception message is :" + exception.getMessage());
Log.e(TAG, "Status code = " + statusCode);
}
});
}
The output is
E/MapsActivity: request for place with ID = ChIJTaKjynxesBQREvi1CU5QUFg
E/MapsActivity: request for place with ID = jhDdW1odXJpeWV0IE1haGFsbGVzaSwgVXp1biBTay4sIEV6aW5lL8OHYW5ha2thbGUsIFR1cmtleSIuKiwKFAoSCakQkmN8XrAUEVkLpNK_F4IJEhQKEgmFzKyYe16wFBGSjU7F2ooIIg
E/MapsActivity: request for place with ID = hIJy9YVxdxpsBQRq0-xUVJdZQ8
E/MapsActivity: placeID renewed
E/MapsActivity: placeID old = EjhDdW1odXJpeWV0IE1haGFsbGVzaSwgVXp1biBTay4sIEV6aW5lL8OHYW5ha2thbGUsIFR1cmtleSIuKiwKFAoSCakQkmN8XrAUEVkLpNK_F4IJEhQKEgmFzKyYe16wFBGSjU7F2ooIIg
E/MapsActivity: placeID new = Ei5DdW1odXJpeWV0LCBVenVuIFNrLiwgRXppbmUvw4dhbmFra2FsZSwgVHVya2V5Ii4qLAoUChIJqRCSY3xesBQRWQuk0r8XggkSFAoSCYXMrJh7XrAUEZKNTsXaiggi
E/MapsActivity: Place with ID hIJy9YVxdxpsBQRq0-xUVJdZQ8 not found
E/MapsActivity: Exception message is :9012: INVALID_REQUEST
E/MapsActivity: Status code = 9012
E/MapsActivity: Place found: ChIJTaKjynxesBQREvi1CU5QUFg
And it seems that the status code 9102 is for place not found.
I'm developing an app and saving some strings like postedAtTime, postedBy, postedOnDate in Firebase database. I want to save the GeoFire coordinates in the same node in which all the above string are saved, so that later I can do query, easily.
Here's the path to which I'm saving all the strings:
databaseReferenceHRequests = firebaseDatabase.getReferenceFromUrl("https://appName-e1a35.firebaseio.com/requests/");
This is how I'm saving it:
// in onButtonClicked method:
postNewRequest(null, imageUID, MainActivity.userName.getText().toString(), time, date, utcFormatDateTime, MainActivity.userEmail.getText().toString(), geoFire);
// the method:
public void postNewRequest(Bitmap bitmap, String imageUIDh, String postedBy, String postedAtTime, String postedOnDate, String utcFormatDateTime, String userEmail, GeoFire geoFire) {
HRequest hRequest = new HelpRequest(null, imageUIDh, postedBy, postedAtTime, postedOnDate, utcFormatDateTime, userEmail, geoFire);
databaseReferenceHRequests.push().setValue(hRequest);
}
Here's how it is getting saved in the database:
What I want is to save the GeoFire coordinates in the same node, which is -KLIoLUsI0SpQZGpV1h4 here. This is just a push ID and it gets generated randomly.
I tried it by giving this reference:
geoFire = new GeoFire(firebaseDatabase.getReferenceFromUrl("https://appName-e1a35.firebaseio.com/requests/"));
And then pushing it with other items as shown above. But, this saved only GeoFire coordinates and not the other items under the node requests.
So, what should be my GeoFire reference so that it gets saved along with all the data in the same node?
What is going wrong here? Please let me know.
Frank's answer is correct, but I want to give an example.
Your database structure should be like this.
{
"items" : {
<itemId> : {
"someData" : "someData",
...
}
},
"items_location" : {
<itemId> : {
<geofireData> ...
}
}
}
To get the data, first you need to do GeoQuery at items_location node and then get the data on the onKeyEntered method. The parameter key is itemId from my example.
geoFire = new GeoFire(FirebaseDatabase.getInstance().getReference().child("items_location");
geoQuery = geoFire.queryAtLocation(geoLocation), radius);
geoQuery.addGeoQueryEventListener(new GeoQueryEventListener() {
#Override
public void onKeyEntered(String key, GeoLocation location) {
//retrieve data
}
};
Hope this helps.
EDIT
How to push the item and set the geofire data.
String itemId = ref.child("items").push().getKey();
ref.child("items").child(itemId).setValue(item);
geoFire = new GeoFire(ref.child("items_location"));
geoFire.setLocation(itemId, new GeoLocation(lattitude, longitude));
EDIT Save the item data and geofire data in one API call
GeoHash geoHash = new GeoHash(new GeoLocation(latitude, longitude));
Map<String, Object> updates = new HashMap<>();
updates.put("items/" + itemId, item);
updates.put("items_location/" + itemId + "/g", geoHash.getGeoHashString());
updates.put("items_location/" + itemId + "/l", Arrays.asList(latitude, longitude));
ref.updateChildren(updates);
When you use Geofire, you have two lists of data:
a list of items with their regular properties
a list of geohash indexes and their associated keys, which you query through Geofire
You use the keys to get from the Geoquery results to the regular items. That's why the events for Geofire are called "Key Entered", "Key Exited", etc.
Trying to store them in one node is a bad idea, since you're mixing mostly static data (the properties of your items) with highly volatile data (the geo-location information). Separating the two out leads to better performance, which is why Geofire enforces it.
While there may be use-cases where the properties and geo-data are equally dynamic/static, GeoFire does not support keeping the geo-data and other properties in a single location.
You can use Firebase functions to enter it for you on every new entry
let functions = require('firebase-functions');
let GeoFire = require('geofire');
exports.testLocation = functions.database.ref('/items/{item}').onWrite(event => {
let data = event.data.val();
console.log(data);
console.log(event.params.item);
if (data.location && data.location.coords) {
console.log('Update GeoFire');
let ref = event.data.adminRef.parent.parent.child('/items_locations'));
let key = event.params.test;
let location = [data.location.coords.latitude, data.location.coords.longitude]);
let geoFire = new GeoFire(ref);
geoFire.set(key, location).then(() => {
console.log('Update succesfull');
}).catch(error => {
console.log(error);
});
}
}
For those more recently coming to this post with the same question, this is possible with the Geofirestore library, which supports Geofire for apps built on top of the Firebase Firestore database.
I need to make a programm in JavaFX where I need to manage a movie library. I have a tableview that is filled by an observable list. Then I made bindings between the Properties and the Textboxes or Labels next to the tableview. Now the problem is, I have also pictures that are related to the movies, like cinema posters. and at the moment I just use a hardcoded one:
imgPoster = new Image(getClass().getResourceAsStream("../resources/images/posters/" + 5 + ".jpg"));
In the datafile there is one column with the ID of the movie and the same number is also the picture for it. So I need to replace that "5" in the sample code with a binding or so that it actively changes the picture as soon as I click on a row in the tableview.
How do I do that?
edit:
After the first comment i did that:
imgOscars = new Image(getClass().getResourceAsStream("../resources/images/oscar/Oscar-logo.png"));
oscars = new ImageView(imgOscars);
oscars.setPreserveRatio(true);
oscars.setFitHeight(45);
but question stays
Either add a listener to the selected item property in the table (I am just guessing at the model you are using for the table):
TableView<Movie> table = ... ;
table.getSelectionModel().selectedItemProperty().addListener((obs, oldSelectedMovie, newSelectedMovie) -> {
if (newSelectedMovie == null) {
oscars.setImage(null);
} else {
// get image from new selection
Image image = new Image(getClass().getResourceAsStream("../resources/images/oscar/"+newSelectedMoviegetId()+".png"));
oscars.setImage(image);
}
});
or use a binding:
oscars.imageProperty().bind(Bindings.createObjectBinding(() -> {
if (table.getSelectionModel().getSelectedItem() == null) {
return null ;
} else {
Movie selected = table.getSelectionModel().getSelectedItem();
return new Image(getClass().getResourceAsStream("../resources/images/oscar/"+selected.getId()+".png")); ;
},
table.getSelectionModel().selectedItemProperty());