1

I have an issue with onMapReady. When i pass an address like this:

LatLng myAddressCoordinates = getLocationFromAddress("Piazza Ferretto Mestre");

Both map and marker work fine but when i pass the address reading from a textview:

LatLng myAddressCoordinates = getLocationFromAddress(TVStringa.getText().toString());

I throw this exception:

java.lang.IllegalArgumentException: latlng cannot be null - a position is required.
        at com.google.android.gms.maps.model.MarkerOptions.position(Unknown Source:6)
        at com.example.alex.alfa0.schedaIntervento.onMapReady(schedaIntervento.java:199)
        at com.google.android.gms.maps.zzaj.zza(Unknown Source:7)
        at com.google.android.gms.maps.internal.zzaq.onTransact(Unknown Source:38)
        at android.os.Binder.transact(Binder.java:627)
        at gl.b(:com.google.android.gms.DynamiteModulesB@11580470:20)
        at com.google.android.gms.maps.internal.bf.a(:com.google.android.gms.DynamiteModulesB@11580470:5)
        at com.google.maps.api.android.lib6.impl.bc.run(:com.google.android.gms.DynamiteModulesB@11580470:5)
        at android.os.Handler.handleCallback(Handler.java:790)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6494)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)

Line 199:

mapView.addMarker(new MarkerOptions().position(myAddressCoordinates).title(Indirizzo));

My getLocationFromAddress method:

public LatLng getLocationFromAddress(String strAddress) {
        /**
         * A class for handling geocoding and reverse geocoding.
         * Geocoding is the process of transforming a street address or other description of a location into a (latitude, longitude) coordinate.
         * Reverse geocoding is the process of transforming a (latitude, longitude) coordinate into a (partial) address.
         * The amount of detail in a reverse geocoded location description may vary,
         * for example one might contain the full street address of the closest building,
         * while another might contain only a city name and postal code.
         *
         * See more at: https://developer.android.com/reference/android/location/Geocoder.html
         */
        Geocoder coder = new Geocoder(this);

        //A list because of getFromLocationName will return a list of address depending on how many result I want
        List<Address> address;
        LatLng p1 = null;

        try {
            /**
             * List<Address> getFromLocationName (String locationName, int maxResults)             *
             * | locationName | String: |           a user-supplied description of a location                      |
             * | maxResults   |   int:  |max number of results to return. Smaller numbers (1 to 5) are recommended |
             * See more at: https://developer.android.com/reference/android/location/Geocoder.html
             */
            address = coder.getFromLocationName(strAddress, 2);
            if (address == null) {
                return null;
            }            //I take just the first result even if I've got list of 5 different LAT;LNG results
            Address location = address.get(0);
            location.getLatitude();
            location.getLongitude();
            p1 = new LatLng(location.getLatitude(), location.getLongitude());
            return p1;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return p1;
    }

Complete class code:

public class schedaIntervento extends AppCompatActivity implements OnMapReadyCallback {
    TextView TVNome, TVID, TVNomeP, TVCognome, TVVia, TVNumero, TVCitta, TVCap, TVChiamata, TVOperatore, TVCodice, TVData, TVStringa;
    String Nomee, Indirizzo;
    GoogleMap mapView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_scheda_intervento);
        TVNome = findViewById(R.id.TVNome);
        TVID =  findViewById(R.id.TVID);
        TVNomeP = findViewById(R.id.TVNomeP);
        TVCognome = findViewById(R.id.TVCognome);
        TVVia=findViewById(R.id.TVVia);
        TVNumero = findViewById(R.id.TVNumero);
        TVCitta = findViewById(R.id.TVCitta);
        TVCap = findViewById(R.id.TVCap);
        TVChiamata = findViewById(R.id.TVChiamata);
        TVOperatore = findViewById(R.id.TVOperatore);
        TVCodice = findViewById(R.id.TVCodice);
        TVData = findViewById(R.id.TVData);
        TVStringa = findViewById(R.id.TVStringa);
        Nomee = getUsername();
        String url = "myurlforapi";
        getJSON(url);
        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.mapView);
        mapFragment.getMapAsync( this);
    }

    private String getUsername() {
        Intent i = getIntent();
        String j = i.getStringExtra("Username");
        TVNome.setText(j);
        return j;
    }

    private void getJSON(final String urlWebService) {

        class GetJSON extends AsyncTask<Void, Void, String> {
            @Override
            protected void onPreExecute() {
                super.onPreExecute();
            }
            @Override
            protected void onPostExecute(String s) {
                super.onPostExecute(s);
                try {
                    loadIntoTextView(s);
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }

            @Override
            protected String doInBackground(Void... voids) {
                try {
                    URL url = new URL(urlWebService);
                    HttpURLConnection con = (HttpURLConnection) url.openConnection();
                    con.setRequestMethod("POST");
                    con.setDoInput(true);
                    con.setDoOutput(true);
                    Uri.Builder builder = new Uri.Builder()
                            .appendQueryParameter("Username", Nomee);
                    String query = builder.build().getEncodedQuery();
                    OutputStream os = con.getOutputStream();
                    BufferedWriter writer = new BufferedWriter(
                            new OutputStreamWriter(os, "UTF-8"));
                    writer.write(query);
                    writer.flush();
                    writer.close();
                    os.close();
                    con.connect();

                    StringBuilder sb = new StringBuilder();
                    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(con.getInputStream()));
                    String json;

                    while ((json = bufferedReader.readLine()) != null) {
                        sb.append(json + "\n");
                    }
                    return sb.toString().trim();
                } catch (Exception e) {
                    return null;
                }
            }
        }
        GetJSON getJSON = new GetJSON();
        getJSON.execute();
    }

    private void loadIntoTextView(String json) throws JSONException {
        JSONArray jsonArray = new JSONArray(json);
        for (int i = 0; i < jsonArray.length(); i++) {
            JSONObject obj = jsonArray.getJSONObject(i);
            TVNomeP.setText(obj.getString("Nome"));
            TVCognome.setText(obj.getString("Cognome"));
            TVVia.setText(obj.getString("Via"));
            TVNumero.setText(obj.getString("Numero"));
            TVCitta.setText(obj.getString("Citta"));
            TVCap.setText(obj.getString("CAP"));
            TVChiamata.setText(obj.getString("Motivo_chiamata"));
            TVOperatore.setText(obj.getString("Operatore"));
            TVCodice.setText(obj.getString("codice"));
            TVData.setText(obj.getString("Data_di_nascita"));
            Indirizzo = "Via " + TVVia.getText().toString() + " " + TVNumero.getText().toString() + " " + TVCitta.getText().toString();
            TVStringa.setText(Indirizzo);
        }
    }


    public LatLng getLocationFromAddress(String strAddress) {
        /**
         * A class for handling geocoding and reverse geocoding.
         * Geocoding is the process of transforming a street address or other description of a location into a (latitude, longitude) coordinate.
         * Reverse geocoding is the process of transforming a (latitude, longitude) coordinate into a (partial) address.
         * The amount of detail in a reverse geocoded location description may vary,
         * for example one might contain the full street address of the closest building,
         * while another might contain only a city name and postal code.
         *
         * See more at: https://developer.android.com/reference/android/location/Geocoder.html
         */
        Geocoder coder = new Geocoder(this);

        //A list because of getFromLocationName will return a list of address depending on how many result I want
        List<Address> address;
        LatLng p1 = null;

        try {
            /**
             * List<Address> getFromLocationName (String locationName, int maxResults)             *
             * | locationName | String: |           a user-supplied description of a location                      |
             * | maxResults   |   int:  |max number of results to return. Smaller numbers (1 to 5) are recommended |
             * See more at: https://developer.android.com/reference/android/location/Geocoder.html
             */
            address = coder.getFromLocationName(strAddress, 2);
            if (address == null) {
                return null;
            }            //I take just the first result even if I've got list of 5 different LAT;LNG results
            Address location = address.get(0);
            location.getLatitude();
            location.getLongitude();
            p1 = new LatLng(location.getLatitude(), location.getLongitude());
            return p1;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return p1;
    }

    @Override
    public void onBackPressed() {
        super.onBackPressed();
        this.finish();
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {
        this.mapView = googleMap;
        /*LatLng sydney = new LatLng(-33.852, 151.211);
        googleMap.addMarker(new MarkerOptions().position(sydney)
                .title("Marker in Sydney"));
        googleMap.moveCamera(CameraUpdateFactory.newLatLng(sydney));*/


        LatLng myAddressCoordinates = getLocationFromAddress(TVStringa.getText().toString());
        //LatLng myAddressCoordinates = getLocationFromAddress("Piazza Ferretto Mestre");
        mapView.addMarker(new MarkerOptions().position(myAddressCoordinates).title(Indirizzo));
        mapView.moveCamera(CameraUpdateFactory.newLatLngZoom(myAddressCoordinates, 16));
    }
}
Basil Battikhi
  • 2,638
  • 1
  • 18
  • 34
Alex
  • 23
  • 5
  • does your text view have any text? Also im surprised how this code is even compiled! find View By Id returning View and not text view which is required to add a casting – Basil Battikhi Apr 11 '18 at 15:57
  • Are you setting the ResultReceiver? Geocode is an async operation. – Pedro Varela Apr 11 '18 at 15:58
  • This most likely means that the Geocoder is not return a result and your method returns null in that case. "Returns null or empty list if no matches were found or there is no backend service available." - so you need to handle that case. –  Apr 11 '18 at 15:59
  • @BasilBattikhi yes but when i try to read it from onMapReady it's empty. – Alex Apr 11 '18 at 15:59
  • hi.. has your android app's sigining key is already added in Google Project for which the geo coding api is enabled ? – Relsell Apr 11 '18 at 16:01
  • So log the result of TVStringa.getText().toString() and post it - again your code still needs to handle the null result possibility. –  Apr 11 '18 at 16:02
  • @Alex for sure you will, have you tried to check if the textview is null ? you need to add Casting ! I'm surprised how this code is compiled ! – Basil Battikhi Apr 11 '18 at 16:05
  • I'd guess it has "Via " as a prefix - is that what you intended. See loadIntoTextView. –  Apr 11 '18 at 16:05
  • The TextView it isn't null, because i can se the text on the app but in the onMapReady it's null and idk why. I'm learning Java and Android and i'm a little confused. Sorry for the spaghetti code. – Alex Apr 11 '18 at 16:09
  • You need to wait for the background task to complete - this task populates the text view - so you have a race condition : map ready vs. json load. –  Apr 11 '18 at 16:11
  • @Andy I had thought about it but I wasn't sure it was that. Maybe i need to start the map condition 2-3 seconds later? Or i need to use a thread? – Alex Apr 11 '18 at 16:15
  • No because you don't know which will ever complete last. Let me post an answer which may help. –  Apr 11 '18 at 16:16

1 Answers1

0

Aside from general error handling, you'll need to coordinate the map load and JSON load such that when JSON load is complete the map is available for use.

This is just one way to solve that race condition.

Remove (moving it to onMapReady) the call to getJSON(url) from your onCreate

Modify your onMapReady to initiate the JSON load (and remove the getLocationFromAddress call and map ops):

public void onMapReady(GoogleMap googleMap) {
   this.mapView = googleMap;
   getJSON(url);
 }

Update the postExecute of your AsyncTask:

protected void onPostExecute(String s) {
    super.onPostExecute(s);
    try {
        loadIntoTextView(s);

        // MOVED FROM onMapReady
        LatLng myAddressCoordinates = getLocationFromAddress(TVStringa.getText().toString());
          //LatLng myAddressCoordinates = getLocationFromAddress("Piazza Ferretto Mestre");
          mapView.addMarker(new MarkerOptions().position(myAddressCoordinates).title(Indirizzo));
          mapView.moveCamera(CameraUpdateFactory.newLatLngZoom(myAddressCoordinates, 16));

    } catch (JSONException e) {
        e.printStackTrace();
    }
}