0 komentarzy

Geolokalizacja – translacja adres <-> współrzędne

Sierpień 13, 2011 Geolokalizacja Tutoriale

Android SDK, oprócz typowych narzędzi związanych z geolokalizacją (dostęp do usług i modułów lokalizacyjnych, pobieranie współrzędnych, informacje o prędkości/odległości/itp.) dostarcza nam bardzo ciekawego narzędzia, które potrafi tłumaczyć współrzędne geograficzne na prawdziwy adres (i odwrotnie). Klasa, która jest odpowiedzialna za tego typu czynności nazywa się Geocoder, natomiast proces tłumaczenia to Geocoding (tłumaczenie współrzędnych geograficznych na adres) lub Reverse Geocoding (adres na współrzędne geograficzne).

W tym artykule stworzymy niewielką aplikację, która wykorzystuje zarówno Geocoding jak i Reverse Geocoding.

Przygotowania

Oto kod źródłowy layoutu, jaki zostanie wykorzystany w naszej aplikacji:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical"
	android:layout_width="fill_parent"
	android:layout_height="fill_parent">
	<ScrollView
		android:layout_height="fill_parent"
		android:layout_width="fill_parent">
		<LinearLayout
			android:layout_height="fill_parent"
			android:layout_width="fill_parent"
			android:orientation="vertical">
			<TextView
				android:layout_height="wrap_content"
				android:layout_width="wrap_content"
				android:text="From address to geolocation:" />
			<EditText
				android:layout_height="wrap_content"
				android:id="@+id/etAddress"
				android:hint="Type address here"
				android:layout_width="fill_parent" />
			<Button
				android:layout_width="wrap_content"
				android:id="@+id/btnAddressToGeo"
				android:text="Get location"
				android:layout_height="wrap_content" />
			<TextView
				android:layout_width="wrap_content"
				android:layout_height="wrap_content"
				android:text="From geolocation to address"
				android:layout_marginTop="10dp" />
			<EditText
				android:layout_height="wrap_content"
				android:id="@+id/etLatitude"
				android:layout_width="fill_parent"
				android:hint="Type latitude"
				android:inputType="numberDecimal" />
			<EditText
				android:layout_height="wrap_content"
				android:hint="Type longitude"
				android:id="@+id/etLongitude"
				android:layout_width="fill_parent"
				android:inputType="numberDecimal" />
			<Button
				android:layout_width="wrap_content"
				android:layout_height="wrap_content"
				android:id="@+id/btnGeoToAddress"
				android:text="Get address" />
			<Button
				android:layout_width="wrap_content"
				android:layout_height="wrap_content"
				android:id="@+id/btnFromYourPos"
				android:text="Get your address" />
			<TextView
				android:layout_width="wrap_content"
				android:layout_height="wrap_content"
				android:layout_gravity="center"
				android:text="Informations"
				android:textSize="20sp"
				android:layout_marginTop="10dp" />
			<ScrollView
				android:layout_width="fill_parent"
				android:layout_height="fill_parent">
				<TextView
					android:layout_width="wrap_content"
					android:layout_height="wrap_content"
					android:text="---"
					android:id="@+id/tvInformations" />
			</ScrollView>
		</LinearLayout>
	</ScrollView>
</LinearLayout>

Aplikacja będzie wymagała pozwolenia na dostęp do sieci (skąd pobierane będą informacje potrzebne do translacji) oraz dostęp do dokładnej pozycji:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />

Oto kod źródłowy Aktywności, od którego zaczniemy tworzyć naszą aplikację:

public class MainActivity extends Activity {
	private EditText etAddress;
	private EditText etLatitude;
	private EditText etLongitude;
	private Button btnAddressToGeo;
	private Button btnGeoToAddress;
	private Button btnFromYourPos;
	private TextView tvInformations;

	private Geocoder geocoder;
	private LocationManager locationManager;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		etAddress = (EditText) findViewById(R.id.etAddress);
		etLatitude = (EditText) findViewById(R.id.etLatitude);
		etLongitude = (EditText) findViewById(R.id.etLongitude);
		btnAddressToGeo = (Button) findViewById(R.id.btnAddressToGeo);
		btnGeoToAddress = (Button) findViewById(R.id.btnGeoToAddress);
		btnFromYourPos = (Button) findViewById(R.id.btnFromYourPos);
		tvInformations = (TextView) findViewById(R.id.tvInformations);
		fillSampleData();
		initButtonsOnClick();
		locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
		geocoder = new Geocoder(this, Locale.getDefault());
	}

	private void fillSampleData() {
		etLatitude.setText("50.0544530059");
		etLongitude.setText("19.93668526411");
		etAddress.setText("Wawel");
	}

	private void initButtonsOnClick() {
		OnClickListener listener = new OnClickListener() {
			public void onClick(View v) {
				switch (v.getId()) {
				case R.id.btnGeoToAddress:
					geolocationToAddress();
					break;
				case R.id.btnFromYourPos:
					currentLocationToAddress();
					break;
				case R.id.btnAddressToGeo:
					addressToGeolocation();
					break;
				default:
					break;
				}
			}
		};
		btnGeoToAddress.setOnClickListener(listener);
		btnFromYourPos.setOnClickListener(listener);
		btnAddressToGeo.setOnClickListener(listener);
	}

	private void showToast(String msg) {
		Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
	}
}
  • linijka 30 i 47 to obiekt Geocoder, którego konstruktor przyjmuje dwa argumenty: kontekst aplikacji oraz obiekt Locale na podstawie którego można będzie określić na jaki język mają być tłumaczone adresy.
  • linijka 44 – tutaj wypełniamy pola tekstowe jakimiś przykładowymi danymi, aby nie robić tego później ręcznie. ;)
  • linijki 61, 64, 67 – te metody zaimplementujemy już za chwilę.

Geocoding

Do przetłumaczenia lokalizacji na adres wykorzystujemy metodę getFromLocation(double, double, int) wywoływaną na obiekcie klasy Geocoder. Agrumentami są tu kolejno:

  • szerokość geograficzna (latitude)
  • dlugość geograficzna (longitude)
  • ilość zwróconych adresów.

Wynikiem zwracanym przez powyższą metodę jest lista obiektów typu Address, który w Androidzie jest odpowiedzialny za przechowywanie informacji adresowych.

Oto implementacja metody wyświetlającej adres na podstawie podanych współrzędnych:

	private void geolocationToAddress() {
		double latitude = new Double(etLatitude.getText().toString());
		double longitude = new Double(etLongitude.getText().toString());
		String result = getAddressFrom(latitude, longitude);
		tvInformations.setText(result);
	}

	private String getAddressFrom(double latitude, double longitude) {
		String result = "Geolocation address:\n";
		try {
			List<Address> addresses = geocoder.getFromLocation(latitude, longitude, 1);			
			for (Address address : addresses) {
				for (int i = 0, j = address.getMaxAddressLineIndex(); i <= j; i++) {
					result += address.getAddressLine(i) + "\n";
				}
				result += "\n\n";
			}
		} catch (IOException e) {
			showToast(e.toString());
		}
		return result;
	}

Blok try/catch zabezpiecza nas przed wyjątkiem rzucanym przez metodę getFromLocation(…) w momencie braku połączenia z siecią (lub innych problemów wejścia/wyjścia. Metoda zwraca listę dostępnych adresów położonych w pobliżu podanych współrzędnych.

Kiedy mamy już nasze adresy, możemy skorzystać z metody getAddressLine(int), która zwraca nam konkretną linijkę adresu zapisanego w obiekcie Address. Po więcej szczegółów odsyłam do dokumentacji (link).

Drugi przykład metoda zwracająca 3 dostępne adresy znajdujące się wokół ostatnio zapisanej lokalizacji w module GPS:

	private void currentLocationToAddress() {
		Location location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
		String result = getAddressFrom(location);
		tvInformations.setText(result);
	}

	private String getAddressFrom(Location location) {
		String result = "Your address:\n";
		try {
			List<Address> addresses = geocoder.getFromLocation(
					location.getLatitude(),	location.getLongitude(), 3);
			for (Address address : addresses) {
				for (int i = 0, j = address.getMaxAddressLineIndex(); i <= j; i++) {
					result += address.getAddressLine(i) + "\n";
				}
				result += "\n\n";
			}
		} catch (Exception e) {
			showToast(e.toString());
		}
		return result;
	}

Jak widać, kod nie różni się zbytnio od poprzedniej metody.

Reverse Geocoding

Jak wspominałem, Geocoder pozwala również na tłumaczenie konkretnego adresu (lub jakiegoś obiektu geograficznego) na współrzędne. Odbywa się to w momencie wywołania metody getFromLocationName(String, int), której argumentami są:

  • Adres, lub nazwa konkretnego miejsca,
  • Ilość zwróconych adresów.

Również i tutaj zwracana jest lista obiektów typu Address, która zawiera dane w jakiś sposób związane z tym co podaliśmy w pierwszym argumencie metody. Tym razem jednak zwrócę uwagę na to, że obiekt klasy Address posiada również dwie metody zwracające długość oraz szerokość geograficzną i to właśnie za ich pomocą możemy pobrać konkretne współrzędne.

Oto kod źródłowy metody, która zwróci nam współrzędne geograficzne obiektu, którego nazwę wpiszemy w polu tekstowym:

	private void addressToGeolocation() {
		String location = etAddress.getText().toString();
		String result = getAddressFrom(location);
		tvInformations.setText(result);
	}

	private String getAddressFrom(String locationStr) {
		String result = "Near locations:\n";
		try {
			List<Address> addresses = geocoder.getFromLocationName(locationStr, 5);
			for (Address address : addresses) {
				for (int i = 0, j = address.getMaxAddressLineIndex(); i <= j; i++) {
					result += address.getAddressLine(i) + "\n";
				}
				result += "Lat: " + address.getLatitude() + "\n"
						+ "Lon: " + address.getLongitude()
						+ "\n\n";
			}
		} catch (IOException e) {
			showToast(e.toString());
		}
		return result;
	}

Zrzuty ekranu

Kompletny kod źródłowy

Cały projekt dostępny jest na naszym Githubie – HelloGeocoding.

MainActivity.java

package pl.froger.hello.geocoding;

import java.io.IOException;
import java.util.List;
import java.util.Locale;

import android.app.Activity;
import android.content.Context;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.location.LocationManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {
	private EditText etAddress;
	private EditText etLatitude;
	private EditText etLongitude;
	private Button btnAddressToGeo;
	private Button btnGeoToAddress;
	private Button btnFromYourPos;
	private TextView tvInformations;

	private Geocoder geocoder;
	private LocationManager locationManager;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		etAddress = (EditText) findViewById(R.id.etAddress);
		etLatitude = (EditText) findViewById(R.id.etLatitude);
		etLongitude = (EditText) findViewById(R.id.etLongitude);
		btnAddressToGeo = (Button) findViewById(R.id.btnAddressToGeo);
		btnGeoToAddress = (Button) findViewById(R.id.btnGeoToAddress);
		btnFromYourPos = (Button) findViewById(R.id.btnFromYourPos);
		tvInformations = (TextView) findViewById(R.id.tvInformations);
		fillSampleData();
		initButtonsOnClick();
		locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
		geocoder = new Geocoder(this, Locale.getDefault());
	}

	private void fillSampleData() {
		etLatitude.setText("50.0544530059");
		etLongitude.setText("19.93668526411");
		etAddress.setText("Wawel");
	}

	private void initButtonsOnClick() {
		OnClickListener listener = new OnClickListener() {
			public void onClick(View v) {
				switch (v.getId()) {
				case R.id.btnGeoToAddress:
					geolocationToAddress();
					break;
				case R.id.btnFromYourPos:
					currentLocationToAddress();
					break;
				case R.id.btnAddressToGeo:
					addressToGeolocation();
					break;
				default:
					break;
				}
			}
		};
		btnGeoToAddress.setOnClickListener(listener);
		btnFromYourPos.setOnClickListener(listener);
		btnAddressToGeo.setOnClickListener(listener);
	}

	private void showToast(String msg) {
		Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
	}

	private void geolocationToAddress() {
		double latitude = new Double(etLatitude.getText().toString());
		double longitude = new Double(etLongitude.getText().toString());
		String result = getAddressFrom(latitude, longitude);
		tvInformations.setText(result);
	}

	private String getAddressFrom(double latitude, double longitude) {
		String result = "Geolocation address:\n";
		try {
			List<Address> addresses = geocoder.getFromLocation(latitude, longitude, 1);			
			for (Address address : addresses) {
				for (int i = 0, j = address.getMaxAddressLineIndex(); i <= j; i++) {
					result += address.getAddressLine(i) + "\n";
				}
				result += "\n\n";
			}
		} catch (IOException e) {
			showToast(e.toString());
		}
		return result;
	}

	private void currentLocationToAddress() {
		Location location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
		String result = getAddressFrom(location);
		tvInformations.setText(result);
	}

	private String getAddressFrom(Location location) {
		String result = "Your address:\n";
		try {
			List<Address> addresses = geocoder.getFromLocation(
					location.getLatitude(),	location.getLongitude(), 3);
			for (Address address : addresses) {
				for (int i = 0, j = address.getMaxAddressLineIndex(); i <= j; i++) {
					result += address.getAddressLine(i) + "\n";
				}
				result += "\n\n";
			}
		} catch (Exception e) {
			showToast(e.toString());
		}
		return result;
	}

	private void addressToGeolocation() {
		String location = etAddress.getText().toString();
		String result = getAddressFrom(location);
		tvInformations.setText(result);
	}

	private String getAddressFrom(String locationStr) {
		String result = "Near locations:\n";
		try {
			Log.d("ABCDDDDD", "a");
			List<Address> addresses = geocoder.getFromLocationName(locationStr, 5);
			Log.d("ABCDDDDD", "b");
			for (Address address : addresses) {
				for (int i = 0, j = address.getMaxAddressLineIndex(); i <= j; i++) {
					result += address.getAddressLine(i) + "\n";
				}
				result += "Lat: " + address.getLatitude() + "\n"
						+ "Lon: " + address.getLongitude()
						+ "\n\n";
			}
		} catch (IOException e) {
			Log.d("ABCDDDDD", e.toString());
			showToast(e.toString());
		}
		return result;
	}
}

Komentarze (0) Subskrybuj

Prześlij komentarz

Zaloguj się lub skorzystaj z profilu:

[rpxlogin redirect="http://www.android4devs.pl" prompt="" style="large"]

Możesz również zostawić komentarz bez rejestracji, korzystając z poniższego formularza:

Musisz być zalogowany aby móc pisać komentarze.