2016년 8월 4일 목요일

Android Studio - LBS(Location Based Service) 프로그래밍 - 2

이번 포스팅은 Google Map을 활용한 위치기반 서비스 앱 개발이다.

이전 포스팅에서 확인하였듯이  LBS 관련 서비스를 제공하기 위해선 Google Play 서비스 활용이 필수적이다.

[현재의 위치값 가져오기 복습]

Fused Location을 이용해서 위치정보를 받아오는 정보는 아래 3가지 방식을 통해서 받아온다.
 - GPS
 - 통신사 NW
 - Wifi NW

위의 세가지 방식 중 GPS가 가장 정확하지만 배터리 소모가 많고 음영지역이 많다는 단점이 있다. 통신사 NW의 경우 상대적으로 음영지역이 적은 반면에 오차율이 1Km ~ 1.5Km로 너무 넓다는 단점이 있다. Wifi 네트워크를 이용하는 방식은 아직 우리나라에서는 법적으로 제한이 걸려있기 때문에 사용할 수 없다.

Fused Location API를 사용하기 전에 설정을 하면 위의 세가지 방법 중 가장 적절한 방법으로 현재의 위치 값을 가져오게 된다.


[Google Map을 활용]

Web 프로그래밍을할 때 Google Map을 이용하는방법은 정말 간단하다.
<script src=~~>를 통해 Google Map API를 임포트하고 지도를 표시 할 Layout을 설정하고 함수 하나만 콜 하면 될 일이었다.

그렇게 간단하던 Google Map이 안드로이드에서는 상대적으로 엄청 복잡한 과정을 거친다.

Manifest 설정, Fragment를 이용한 화면단 소스코딩 게다가 Android Developer Console에 프로젝트를 등록하여 Authentication Key 값을 생성 및 얻어와야지 비로소 사용할 수 있게된다.


[구현하기]

일단 manifest 파일에 permission 세팅

<permission 
    android:name="com.example.ch5_lbs.permission.MAPS_RECEIVE"
    android:protectionLevel="signature"/>
<uses-permission 
    android:name="com.example.ch5_lbs.permission.MAPS_RECEIVE"/>

<uses-permission 
    android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission 
    android:name="android.permission.INTERNET"/>
<uses-permission 
    android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission 
    android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
<uses-permission 
    android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission 
    android:name="android.permission.ACCESS_COARSE_LOCATION"/>

Google Map 사용을 하기 위해서 키값 입력

<meta-data 
    android:name="com.google.android.maps.v2.API_KEY"
    android:value="AIzaSyDQQICJSD3Rx3m6b0AFXiHJOjEeGWM3Sqw"/>
<meta-data 
    android:name="com.google.android.gms.version"
    android:value="@integer/google_play_services_version"/>
<activity android:name=".MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

Google Map을 표현하기 위해서 Layout.xml에 Fragment를 추가

<fragment
    android:id="@+id/map_view"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1"
    android:name="com.google.android.gms.maps.SupportMapFragment"/>

MainActivity.java

package com.example.ch5_lbs;

import android.location.Location;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.FusedLocationProviderApi;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.UiSettings;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;

import java.text.DecimalFormat;
import java.text.SimpleDateFormat;

/** * OnMapReadyCallback: 지도가 준비된 시점을 잡기위한 callback * 이 시점에 map을 이용하지 않으면 시간차의 문제로 nullpoint등의 에러 발생 * */
public class MainActivity extends AppCompatActivity
        implements GoogleApiClient.ConnectionCallbacks,
                    GoogleApiClient.OnConnectionFailedListener, OnMapReadyCallback {

    TextView providerTextView;
    ImageView onOffImageView;
    TextView timeTextView;
    TextView locationTextView;
    TextView accuracyTextView;

    GoogleApiClient googleApiClient;
    FusedLocationProviderApi fusedLocationProviderApi;
    Location currentlocation;

    GoogleMap map;  // fragment에 지도 뿌리는 view
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        providerTextView = (TextView) findViewById(R.id.txt_location_provider);
        onOffImageView = (ImageView) findViewById(R.id.img_location_on_off);
        timeTextView = (TextView) findViewById(R.id.gps_time);
        locationTextView = (TextView) findViewById(R.id.gps_location);
        accuracyTextView = (TextView) findViewById(R.id.gps_accuracy);

        /**         * Fused API는 실제 google play service라이브러리에서 제공되는 기능으로         * google의 다양한 service를 활용하기 위한 라이브러리이다.         * 그중 locationservice쪽을 이용하겠다고 선언         */        googleApiClient = new GoogleApiClient.Builder(this)
                .addApi(LocationServices.API)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .build();

        fusedLocationProviderApi = LocationServices.FusedLocationApi;
    }

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

    private String getDateTime(long time) {
        if (time == 0)
            return "";

        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        return formatter.format(new java.util.Date(time));
    }

    private String convertDouble(double input) {
        DecimalFormat format = new DecimalFormat(".######");
        return format.format(input);
    }

    /**     * 위치정보 획득후 호출     * 매개변수의 정보대로 다양한 정보 추출하고 화면 출력 역할     */    private void updateInfo(Location location){
        onOffImageView.setImageResource(R.drawable.on);
        timeTextView.setText(getDateTime(location.getTime()));
        locationTextView.setText("LAT:" + convertDouble(location.getLatitude())
                                +" LNG:" + convertDouble(location.getLongitude()));
        accuracyTextView.setText(location.getAccuracy() + " meters");
    }

    /**     * 위치값 획득후에 호출되어 지도제어     */    private void showMap(Location location){
        // 지도에서의 위치는 LatLng로 표현        LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
        // 지도 옵션        CameraPosition position = new CameraPosition.Builder().target(latLng).zoom(16f).build();
        // 지도의 center 위치 이동        map.moveCamera(CameraUpdateFactory.newCameraPosition(position));
        // market 올리기        map.clear();    // 이전 marker지우고 안지우면 중복으로 나옴        map.addMarker(new MarkerOptions().position(latLng).title("Location")
                    .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_BLUE)));
    }

    @Override    protected void onResume() {
        super.onResume();
        googleApiClient.connect();  // 겨리과는 callback함수 호출로        if(map==null){
            ((SupportMapFragment)getSupportFragmentManager().findFragmentById(R.id.map_view)).getMapAsync(this);
        }
    }



    @Override
    protected void onPause() {
        super.onPause();
        if(googleApiClient.isConnected()){
            googleApiClient.disconnect();
        }
    }

    @Override
    public void onConnected(@Nullable Bundle bundle) {
        // 위치값 획득        Location location = fusedLocationProviderApi.getLastLocation(googleApiClient);
        if(location != null){
            currentlocation = location;
            updateInfo(location);
            showMap(location);
        }
    }

    @Override
    public void onConnectionSuspended(int i) {
        toast("onConnectionSuspended");
    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
        toast("onConnectionFailed");
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {
        map = googleMap;
        UiSettings settings = map.getUiSettings();
        settings.setZoomControlsEnabled(true);

        map.setOnInfoWindowClickListener(new GoogleMap.OnInfoWindowClickListener(){
            @Override
            public void onInfoWindowClick(Marker marker) {
                toast("info window click...");
            }
        });
    }
}



댓글 없음:

댓글 쓰기