출처 : http://bugnote.tistory.com/2


안드로이드 프로그래밍을 하던 중 사용자와 사용자간의 거리를 계산해야 할 일이 생겼다.

각 사용자의 위치는 위도와 경도로 알고 있기 때문에 아래 공식을 이용하여 적용했다.

좌표 1(위도,경도)과 좌표 2(위도,경도) 거리 계산 방법

sLat,sLng 는 좌표1(로그인한 사용자)의 위도, 경도 값이다.

double sLat = Double.valueOf(LoginUser.item.lat);
double sLng = Double.valueOf(LoginUser.item.lng);

eLat, eLng는 대상(target)의 위도,경도 값이다.
double eLat = Double.valueOf(item.lat);
double eLng = Double.valueOf(item.lng);

String distance = SocialUtil.calcDistance(sLat, sLng, eLat, eLng);
distanceStr = "● 나와  " + distance +" 거리에 있습니다.";

거리를 구한다음 textview에 표시를 해준다.

socialtxt.setText(distanceStr);

삼각함수를 이용하여 사용자간의 거리를 구하는 핵심 공식은 아래와 같다.

SocialUtil.calcDistance

    public static String calcDistance(double lat1, double lon1, double lat2, double lon2){
     double EARTH_R, Rad, radLat1, radLat2, radDist; 
     double distance, ret;

        EARTH_R = 6371000.0;
        Rad = Math.PI/180;
        radLat1 = Rad * lat1;
        radLat2 = Rad * lat2;
        radDist = Rad * (lon1 - lon2);
        
        distance = Math.sin(radLat1) * Math.sin(radLat2);
        distance = distance + Math.cos(radLat1) * Math.cos(radLat2) * Math.cos(radDist);
        ret = EARTH_R * Math.acos(distance);

        double rslt = Math.round(Math.round(ret) / 1000);
        String result = rslt + " km";
        if(rslt == 0) result = Math.round(ret) +" m";
        
        return result;
    } 

결과 화면은 아래와 같다

출처 :  http://ncube.net/7388

이번에 작업 중인 사이트.. 수시로 정책이 바뀌어서 고생하고 있는.. 사이트에서 반경을 이용한 업체 검색 기능이 필요해서 구글링 좀 하고 잔머리를 좀 굴려서 원하는 기능을 구현했다. 각 지점의 경도와 위도는 네이버 지도API를 이용해 얻어온다. 그런 다음 경도 위도를 DB에 저장하고 Query를 이용해 기준 반경 내 업체를 구하는 식이다.

1. 네이버 지도 API에서 경도, 위도 받아오기
우선 네이버 지도 API를 이용하기 위해서는 Key를 등록해야 한다. http://dev.naver.com/openapi/register 에 접속한다. 로그인이 되어야 정상적으로 이용할 수 있다. 그런 다음 아래 화면처럼 지도키 발급에서 사이트 주소를 입력하고 키를 발급받도록 한다.

그런 다음 경도 위도를 확인하기 위해서 아래 주소로 접속한다. test_key 부분은 위에서 발급 받은 키로 변경한다. 좌표확인URL은 아래와 같다.

더 자세한 사용법은 http://dev.naver.com/openapi/apis/map/javascript_2_0/reference#CT_URL 에서 확인 가능.

아래는 결과 화면이다.

결과는 XML 포맷으로 전달되는 데 x, y 값이 우리가 원하는 값이다. XML 파싱 부분은 검색을 통해서.. ^^;

간단히 내가 사용한 XML Parser는 http://www.criticaldevelopment.net/xml/ 에서 얻을 수 있으며 파서 파일을 PHP 파일에 include 한 후 아래 코드를 이용해 XML에서 좌표를 얻어낸다.

test_key 부분은 위에서 발급받은 키로 변경해야 한다. 그리고 php 파일이 위치한 서버의 도메인과 발급받은 키의 도메인이 일치하지 않으면 실행되지 않는다. www가 붙은 도메인과 붙지 않은 도메인은 서로 다르게 인식되는 듯 하다.

2. MySQL Query를 이용해 기준 반경내 업체 검색
우선 경도 위도를 저장할 필드를 추가해줘야 한다. DATA 타입은 아래와 같이 하면 된다. 필드 명은 적당히 변경.

이제 기준점 (127.0000000, 37.0000000)에서 DB에 저장된 지점의 거리를 계산하는 Query를 작성해보자.

위의 쿼리는 TEST_TABLE에서 기준점에서 반경 5Km 이내의 id를 거리가 가까운 순서로 5개 가져오는 것이다.

참고 : http://code.google.com/intl/ko/apis/maps/articles/phpsqlsearch_v3.html

결과 값이 얻어지면 이제 원하는 기능을 하도록 코드를 작성하면 된다

출처 : http://blog.naver.com/blow1/150106632098


해당 위도와 경도가  주어진 구역반경에 들어오는지 체크 한다.

 

아래는 위,경도를 통한 도분초를 계산하는 방법이며 마지막에 실제 함수를 작성하였다.

 

구글 거리계산기와 아래 함수를 비교하여 보니 정확한 계산치를 확인 할 수 있었다.

 

하지만, 아래 계산법은 우리 나라에 한해서만 적용되어야 할 듯 하다.( 서핑 하다 보니 이러저러한 이유로 지구 전체에 대한 것은 좀더 상세한 계산법이 필요 할 듯 하다)


 lat : 위도 , lon: 경도.

 

 ex>35.3036944

 도 : 35
 분 : 0.3036944 * 60 : int : 18
 초 : (0.3036944 * 60 - 18)*60 : 13.3

 35도 18분 13.3초


  A 지점 : 동경 127도 30분 20초    북위 : 36도 27분 08초 
  B 지점 : 동경 127도 29분 30초    북위 : 36도 28분 00초 
  C 지점 : 경도  00도   1분 10초    위도 : 00도 01분 08초

  경도간 거리 : 0도*88.8 +  1분 * 1.48 + 10초 * 0.025 = 1.73km
  위도간 거리 : 0도*111  +  1분 * 1.85 +   8초 * 0.031 = 2.10km

  두지점간 거리 : 루트 (1.73^2 + 2.10^2) = 2.72km

 

#define DIV_VALUE    10000000

 

int distanceCmp(unsigned int lat1, // 기준 위도,

unsigned int lon1, // 기준 경도,

unsigned int lat2,  // 대상 위도,

unsigned int lon2,  // 대상 경도

unsigned int nCmpLat, // 위도간 거리

unsigned int nCmpLon // 경도간 거리

)

{

/*

위도,경도에 대한 절대값 계산

*/
 double lon = lon1 > lon2  ? (lon1 - lon2)/(double)DIV_VALUE   : (lon2-lon1)/(double)DIV_VALUE;
 double lat =  lat1 > lat2  ? (lat1 - lat2)/(double)DIV_VALUE   : (lat2-lat1)/(double)DIV_VALUE;

 

/*

경도에 대한 도분초및 거리 계산

*/
 int rad = (int)lon;
 int min = (lon-rad)*60;
 double sec = ((lon-rad)*60 - min)*60;
 unsigned int lon_dist, lat_dist;

 lon_dist = ((rad * 88.8) + (min*1.48) + (sec*0.025)) * 1000; // m단위

 

/*

위도에 대한 도분초및 거리 계산

*/

 rad = (int)lat;
 min = (lat-rad)*60;
 sec = ((lat-rad)*60 - min)*60;

 lat_dist = ((rad * 111) + (min*1.85) + (sec*0.031)) * 1000; // m단위

 

 if( nCmpLat == 0 ){ // 원 형태의 구역반경

 // 직선거리만을 조건으로 한다.
  int realDist = sqrt((lon_dist*lon_dist)+(lat_dist*lat_dist));

  if( nCmpLon >= realDist ){
   return 1;
  }
 }else if( nCmpLat >= lat_dist && nCmpLon >= lon_dist ){ // 사각 형태의 구역반경

// 종/횡측 거리안에 들어오는지 확인한다.
  return 1;
 }
  
 return 0;
}


출처 : http://messi.adnaru.com/guide/guide04.html


튜토리얼 결과물 소개

 

튜토리얼의 최종 결과물입니다. 휴대전화를 사용하고 있는 사용자의 위치 주변에 치킨, 중국집, 보쌈, 피자등의 배달 음식점을 찾아주는 애플리케이션입니다. 이 튜토리얼은 다음 좌표계변환 API를 이용해 GPS 위도,경도정보를 주소 정보로 변환한 후에, 네이버 지역정보 API를 통해 주변 배달 음식점 정보를 받아오게 됩니다. 두개의 API를 매쉬업해 주변 배달 음식을 찾는 앱을 만드는 가이드를 따라해봅시다.


가이드를 시작하기에 앞서

주의사항: 이 과정을 꼭 거쳐주세요. 반드시 필요로하는 사항입니다

이 가이드는 Messi 라이브러리를 이용해 다음 쇼핑 API를 기반으로 최저가 검색기 앱을 만드는 가이드를 담은 문서입니다. 가이드를 진행하시기 앞서 아래의 사항들이 필요합니다.

  • ADT또는 안드로이드 스튜디오
  • Android SDK(2.2 Froyo 이상)
  • Messi 라이브러리

안드로이드를 처음 개발하는 개발자분들은, “복사-붙여넣기”코너는 그대로 소스코드를 복사해서 붙여넣으시면 편리합니다. 또한 최종적으로 개발된 코드 결과물을 압축파일로 제공하므로, 코딩하는 과정에서 어려움을 느끼신다면 해당 결과물 파일을 참고해주세요.


Open API 키 발급받기

다음 Open API

다음 DNA(http://dna.daum.net) 에서 로컬형 Open API키를 발행하여 주십시오. 기존에 발행한 키가 있다면 해당 키를 사용하셔도 무관합니다.

발행받을 키는 로컬형 key로 발행하여 주시고, 해당 key를 계속해서 사용하여야 하니 저장해두시는 편이 편리합니다.
또한 다음은 쇼핑, 검색, 컨텐츠, 로컬API키가 모두 다르므로, 특별히 유의해서 발급받아 주십시오.


네이버 Open API

네이버 개발자센터(http://developer.naver.com) 에서 Open API키를 발행하여 주십시오. 기존에 발행한 키가 있다면 해당 키를 사용하셔도 무관합니다.

발행받을 키는 검색 key로 발행하여 주시고, 해당 key를 계속해서 사용하여야 하니 저장해두시는 편이 편리합니다.


라이브러리 연동

안드로이드 프로젝트를 생성하여 주십시오. ADT혹은 안드로이드 스튜디오에서 새 프로젝트를 생성하여 주세요. 프로젝트에 설정된 SDK버전은 기존값을 따라가셔도 무관합니다.

libs에 라이브러리 넣기

해당 프로젝트의 libs 폴더에 messi.jar 파일을 끌어넣어 복사해주십시오. messi.jar 파일은 라이브러리 폴더 상단에 있습니다.


프로젝트와 라이브러리 연동

생성하신 프로젝트에서 messi 라이브러리를 사용하도록 설정하여야 합니다. ADT 기준으로 프로젝트 최상위 폴더에서 오른쪽 마우스를 누른 후 [Properties] – [Java Build path] – [Libraries]로 들어가 주십시오. 그리고 오른쪽에 있는 Add Jar을 눌러 앞서 붙여 넣기 한 라이브러리 파일을 추가해 주십시오.


권한 설정

Messi는 기본적으로 인터넷 연결 권한이 필요합니다. AndroidMenifest.xml에서 인터넷 권한을 추가해주십시오. 아울러 이 프로젝트에선 사용자의 위치정보를 가져오기 위해 위치정보를 사용할 권한과, 전화를 걸기 위한 권한이 필요합니다. 아래의 소스코드를 복사-붙여넣기 하셔도 무관합니다.

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

레이아웃 구성

이제 화면 레이아웃을 구성해 봅시다. 레이아웃 구성은 자유이지만, 가이드라인을 따라하고 싶으면 아래의 내용을 참고하셔도 좋습니다. 레이아웃 구성은 메인페이지와 각 개별 아이템구성으로 나뉘어집니다.

메인페이지

이번 튜토리얼은 각 업종별 조회 버튼과 그 목록 결과를 받아오는 ListView로 구성되어있다고 생각해봅시다. 그리고 각 버튼을 눌렀을때, Messi를 이용하여 검색 결과값을 받아오는 화면 구성으로 진행됩니다. 화면 구성은 자유자재로 바꾸시면서 진행하셔도 무관합니다.

복사-붙여넣기 할때는 activity_main.xml 파일에 붙여넣기 해 주십시오.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="${relativePackage}.${activityClass}" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" 
        android:orientation="horizontal">

        <Button
            android:id="@+id/btn1"
            android:layout_width="match_parent"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:text="치킨" />

        <Button
            android:id="@+id/btn2"
            android:layout_width="match_parent"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:text="중국집" />

        <Button
            android:id="@+id/btn3"
            android:layout_width="match_parent"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:text="피자" />

        <Button
            android:id="@+id/btn4"
            android:layout_width="match_parent"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:text="족발" />

    </LinearLayout>

    <ListView
        android:id="@+id/listView1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >
    </ListView>

</LinearLayout>

개별 아이템 구성

화면 구성을 모두 마쳤으면 가게별 목록 개별 아이템을 만들어봅시다. 이 가이드라인에는 가게의 이름, 주소, 전화번호 정보가 표시됩니다. 아래의 소스코드를 그대로 복사-붙여넣기 하시거나, 아니면 더 창의적이고 멋진 UI를 만드셔도 괜찮습니다. 복사-붙여넣기으로 진행하실 분들은 item_row.xml이라는 레이아웃 파일을 만드신 후 아래의 코드를 붙여넣기 해주세요.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/item_tv1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="기사 제목"
        android:layout_marginTop="10dp"
        android:textAppearance="?android:attr/textAppearanceMedium" />

    <TextView
        android:id="@+id/item_tv2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="기사 내용"
        android:layout_marginBottom="10dp"
        android:textAppearance="?android:attr/textAppearanceSmall" />

</LinearLayout>

레이아웃 구성을 마쳤으면, 이제 소스코드를 작성해봅시다. 이 단계에서는 messi를 활용하여 API를 연동하는 과정을 다룹니다. 모든 과정에 대한 소스코드는 아래에서 다운로드 하실 수 있습니다.

임포트

프로젝트의 메인이 되는 액티비티인 MainAcitivty.java파일로 들어가 주십시오. 그리고 아래의 소스코드를 import 부분에 삽입하여 주십시오.

import com.hhdd.messi.Daum;
import com.hhdd.messi.Naver;
import com.hhdd.messi.event.DaumEventListener;
import com.hhdd.messi.event.NaverEventListener;
import com.hhdd.messi.naver.object.search.LocalObject;

복사-붙여넣기로 진행하셔도 무관합니다. 위의 클래스들은, 다음과 네이버의 API 객체, 결과처리를 통보할 이벤트리스너, 결과값 검색 객체와 관련된 내용입니다.


이벤트 리스너 추가

Messi는 이벤트 기반으로 값을 주고받으며, API 호출 성공시에는 OnResult와 실패시에는 OnFault로 결과값을 보내줍니다. 이벤트 리스너와 연동하기 위해 이벤트 객체를 implements 합시다.

public class MainActivity extends Activity implements NaverEventListener.OnLocalListener

이벤트 객체를 implements 했다면, 결과값을 받아올 OnResult과 실패시 원인을 통보할 OnFalut 메서드가 필요합니다. 키보드에서 Alt키 다음 S키 다음 V키를 눌러 생성하거나, 아니면 아래의 코드를 Oncreate 함수 하단에 복사-붙여넣기하여 주십시오.

private String dong_name = "";
private Naver naver_api;
private Daum daum_api;

@Override
public void onFaultResult(int arg0, String arg1) {
// 실패시 원인과 결과값을 리턴		
}
	
@Override
public void onResult(ArrayList<LocalObject> arg0) {
// 성공시 결과목록을 ArrayList 제너릭 형태로 리턴
}

오픈API 객체 생성

OnCreate 메서드 아래에 Daum 객체를 new로 만들어주십시오. 그리고 이어서 네이버 객체는, 앞서 발급받은 API Key값을 설정하는 부분을 추가합시다. API Key값은 setSearchKey 메서드에 key 값을 넣으면 다시 설정할 필요 없이 반복해서 사용할 수 있습니다. 그리고 다음 객체에도 앞서 발급받은 로컬 API Key 값을 설정하여 주십시오. 다음의 로컬 API Key값은 setLocalKey 메서드로 설정할 수 있습니다. 복사-붙여넣기는 OnCreate 메서드 안에 해주세요.

naver_api = new Naver();
daum_api = new Daum();
daum_api.setLocalKey("다음 로컬API 키");
naver_api.setSearchKey("네이버 검색API 키");
naver_api.setLocalListener(this);
							

각 객체들을 초기화 하는 절차를 마쳤습니다. 이어서, GPS 정보를 받아오는 부분을 추가합시다. 사용자의 GPS 정보는 LocationManager 클래스를 통해 받아올 수 있습니다. 이 튜토리얼에서는 사용자의 네트워크를 기준으로 위치를 찾아보는걸로 진행하겠습니다. LocationManager와 관련되어 더 참고하실 정보가 있다면, 구글 개발자 사이트를 방문하여 참고하여 주십시오. 그리고, LocationManager를 통해 받은 위도, 경도값을 주소 단위로 변환하는 메서드를 실행하고 그 결과값을 받는 리스너를 추가합니다. 위의 처럼 별도로 implements 하지 않고 생성자를 통해 처리하도록 구현해봅시다. 아래의 소스코드를 아까 만든 다음 객체 아래에 복사-붙여넣기 하거나, 아니면 코드를 위의 로직처럼 작성해 주세요.

		daum_api.setCoord2addrListener(new DaumEventListener.OnCoord2addrListener() {
			public void onResult(String arg0, String arg1, String arg2, String arg3) {
				dong_name = arg0;
				naver_api.LocalSearch(dong_name + " 근처 치킨");
			}
			public void onFaultResult(int arg0, String arg1) {
				// TODO Auto-generated method stub
				
			}
		});
        LocationManager manager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        Location location = manager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
        daum_api.Coord2addr(location.getLongitude(), location.getLatitude(), "WGS84");
		

위와 같이 만들면 별도로 implements 하지 않더라도, argument으로 결과값을 처리받을 수 있습니다. OnResult는 결과를 받는 메서드로, 여기서 사용자의 위치 정보를 행정동 주소로 변환한 결과를 알 수 있습니다. 첫번째 파라미터는 행정동이며, 그 뒤로 자치구, 자치시로 이어집니다. 이중에서 행정동 정보만 dong_name 변수에 저장합시다. 그리고 바로 네이버 지역검색 API에 "행정동 근처 치킨"이라는 키워드를 검색해 주변 치킨집 목록을 검색할 수 있게됩니다. 그리고, 앞서 만들어둔 각 배달 음식별 메뉴를 클릭했을때 주소 근처에 있는 가게를 찾도록 하는 로직을 추가합시다.

		Button btn1 = (Button) findViewById(R.id.btn1);
        Button btn2 = (Button) findViewById(R.id.btn2);
        Button btn3 = (Button) findViewById(R.id.btn3);
        Button btn4 = (Button) findViewById(R.id.btn4);
        
        btn1.setOnClickListener(new OnClickListener(){
			@Override
			public void onClick(View v) {
				naver_api.LocalSearch(dong_name + " 근처 치킨");
			}
        });
        
        btn2.setOnClickListener(new OnClickListener(){
			@Override
			public void onClick(View v) {
				naver_api.LocalSearch(dong_name + " 근처 중국집");
			}
        });
        
        btn3.setOnClickListener(new OnClickListener(){
			@Override
			public void onClick(View v) {
				naver_api.LocalSearch(dong_name + " 근처 피자");
			}
        });
        
        btn4.setOnClickListener(new OnClickListener(){
			@Override
			public void onClick(View v) {
				naver_api.LocalSearch(dong_name + " 근처 족발");
			}
        });

어댑터 만들기

위의 과정까지 마치면 자동으로 소셜픽 검색 결과를 통해 뉴스를 검색하고 그 결과값을 다시 OnFsult나 OnResult로 결과값을 알려줍니다. OnResult 호출시에 결과값을 앞서 구상한 ListView에 뿌려줍시다. ListView는 Adapter를 통해 연결할 수 있습니다. Adapter는 아까 만든 사전 검색 개별아이템 결과와 연결되게됩니다. Adapter에 대해 잘 이해가 가지 않는다면 아래의 코드를 복사-붙여넣기하셔서 사용하셔도 무관합니다.

public class ListAdapter extends ArrayAdapter<LocalObject>{
    	public ArrayList<LocalObject> items;
    	
    	public ListAdapter(Context context, int textViewResourceId, ArrayList<LocalObject> items) {
            super(context, textViewResourceId, items);
    		this.items = items;
    	}
    	
    	@Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View v = convertView;
            if (v == null) {
                LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                v = vi.inflate(R.layout.item_row, null);
            }
            
            LocalObject Info = items.get(position);
            
            if (Info != null) {
            	TextView tv1 = (TextView) v.findViewById(R.id.item_tv1);
            	TextView tv2 = (TextView) v.findViewById(R.id.item_tv2);
            	tv1.setText(Info.getTitle());
            	tv2.setText(Info.getAddress()+"\n"+Info.getTelephone());
            }
            return v;
        }
    	
    }

LocalObject 네이버 지역 검색결과를 담는 Object입니다. Object의 구조는 문서에서 살펴볼 수 있습니다. 위 코드에서 getTitle은 가게의 이름을, getAddress는 가게의 주소를, getTelephone은 가게의 전화번호를 가져올 수 있습니다. 그리고, 앞서 만든 어댑터에 붙여넣겠습니다.


어댑터와 연결

어댑터 클래스를 만들었다면, 어댑터와 검색결과값을 연동시켜주어야합니다. 검색결과는 OnResult에서 불러옵니다. 메인 페이지에있는 ListView를 찾은 후 어댑터를 연결하여 완성시켜 줍시다. 아래의 소스코드를 OnResult 함수 안에 그대로 복사-붙여넣기 하셔도 괜찮습니다.

ListView listView = (ListView) findViewById(R.id.listView1);
ListAdapter b1_adapter = new ListAdapter(this, R.layout.item_row, arg0);
listView.setAdapter(b1_adapter);

전화 걸기

이렇게 끝나면 재미가 없죠? 조금 더 새로운 기능을 추가해봅시다. 배달음식을 찾는 앱이니, 바로 전화를 거는 기능도 추가해봅시다. 리스트뷰의 각 아이템을 눌렀을때 발생하는 setOnItemClickListener를 통해 사용자가 선택한 가게로 바로 전화를 걸 수 있도록 하는 로직을 추가합시다. 아래의 소스코드를 어뎁터 연결 부분 밑에 그대로 복사-붙여넣기 하셔도 괜찮습니다.

listView.setOnItemClickListener(new OnItemClickListener(){

			@Override
			public void onItemClick(AdapterView<?> parent, View view,
					int position, long id) {
				LocalObject obj = arg0.get(position);
				Intent callIntent = new Intent(Intent.ACTION_CALL);
				callIntent.setData(Uri.parse("tel:"+obj.getTelephone()));
				startActivity(callIntent);
			}
			
		});

여기까지 따라오시느라 수고가 정말 많으셨습니다! 이제 모든 코드 작성이 완료되었습니다 앱을 실행하면 아래와 같이 나옵니다.

 

앱을 실행하자 마자 바로, 내 주변에 있는 치킨 가게들이 나옵니다. 얏호! 오늘 저녁은 치킨 한마리 어떠신지요? 그런데 이 프로젝트를 진행하면서 아래와 같은 고민을 한번 해보시면서 튜토리얼을 마쳤으면 좋겠습니다.

  • 행정동 단위로 검색하는것이 과연 정확할까요? 중동만 하더라도 서울시 마포구와 경기도 부천시에 있습니다. 조금 더 정확도를 높일 방법이 없을까요?
  • 가게의 정보를 미리 데이터베이스화 해서 검색하는것이 조금 더 빠르지 않을까요? 1년에 얼마나 많은 가게들이 새롭게 문을 열고 닫을까요?
  • GPS 정보 이외에 더 정확하게 사용자의 위치를 받아올 수 있는 방법이 없을까요?

오늘은 여기에서 숙제로 남기고 가겠습니다! Just Do IT!


소스코드 다운로드

이 과정으로 튜토리얼을 마칩니다. 따라오느라 수고가 많았습니다. 혹시 해당 과정에서 문제가 있거나 이해하기 어려운 부분이 있다면 소스코드를 다운받아 한번 살펴봐 주세요.

다운로드

안사 이쁜이  2014-03-12 (수) 17:00
축하드립니다. ;)
냐옹이류님은 안사 이쁜이에 당첨되어 2 포인트 지급되었습니다.

glaxy  2014-03-12 (수) 17:42
베플로 선택된 게시물입니다. 


Google Map Ver.2 현재 위치에 마커 찍는 소스



GibtHub 주소 : https://github.com/bear2u/GoogleMapTestApp.git
public class MainActivity extends FragmentActivity implements LocationListener{
    private GoogleMap mMap;
    private LocationManager locationManager;
    private String provider;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        int googlePlayServiceResult = GooglePlayServicesUtil.isGooglePlayServicesAvailable(MainActivity.this);
        if( googlePlayServiceResult !=   ConnectionResult.SUCCESS){ //구글 플레이 서비스를 활용하지 못할경우 <계정이 연결이 안되어 있는 경우
            //실패
            GooglePlayServicesUtil.getErrorDialog(googlePlayServiceResult, this, 0, new DialogInterface.OnCancelListener()
            {
                @Override
                public void onCancel(DialogInterface dialog)
                {
                    finish();
                }
            }).show();
        }else { //구글 플레이가 활성화 된 경우
            locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
            Criteria criteria = new Criteria();
            provider = locationManager.getBestProvider(criteria, true);

            if (provider == null) {  //위치정보 설정이 안되어 있으면 설정하는 엑티비티로 이동합니다
                new AlertDialog.Builder(MainActivity.this)
                        .setTitle("위치서비스 동의")
                        .setNeutralButton("이동", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                startActivityForResult(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS), 0);
                            }
                        }).setOnCancelListener(new DialogInterface.OnCancelListener() {
                    @Override
                    public void onCancel(DialogInterface dialog) {
                        finish();
                    }
                })
                        .show();
            } else {   //위치 정보 설정이 되어 있으면 현재위치를 받아옵니다

                locationManager.requestLocationUpdates(provider, 1, 1, MainActivity.this); //기본 위치 값 설정
                setUpMapIfNeeded(); //Map ReDraw
            }

            setMyLocation(); //내위치 정하는 함수
        }
    }//onCreate


    private LatLng myLocation;
    double[] myGps;

    private void setMyLocation(){
        mMap.setOnMyLocationChangeListener(myLocationChangeListener);

    }
    Marker mMarker;
    private GoogleMap.OnMyLocationChangeListener myLocationChangeListener = new GoogleMap.OnMyLocationChangeListener() {
        @Override
        public void onMyLocationChange(Location location) {
            LatLng loc = new LatLng(location.getLatitude(), location.getLongitude());
            mMarker = mMap.addMarker(new MarkerOptions().position(loc));
            if(mMap != null){
                mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(loc, 16.0f));
            }
        }
    };

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {//위치설정 엑티비티 종료 후
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode) {
            case 0:
                locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
                Criteria criteria = new Criteria();
                provider = locationManager.getBestProvider(criteria, true);
                if(provider==null){//사용자가 위치설정동의 안했을때 종료
                    finish();
                }else{//사용자가 위치설정 동의 했을때
                    locationManager.requestLocationUpdates(provider, 1L, 2F, MainActivity.this);
                    Log.d("KTH","117 locationMaanger done");
                    setUpMapIfNeeded();
                }
                break;
        }
    }

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

    @Override
    protected void onResume() {
        super.onResume();
        setUpMapIfNeeded();

    }

    @Override
    protected void onPause() {
        super.onPause();
        locationManager.removeUpdates(this);
    }

    private void setUpMapIfNeeded() {
        if (mMap == null) {
            mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.mapView)).getMap();
            if (mMap != null) {
                setUpMap();
            }
        }
    }

    private void setUpMap() {
        mMap.setMyLocationEnabled(true);
        mMap.getMyLocation();

    }

    boolean locationTag=true;

    @Override
    public void onLocationChanged(Location location) {
        if(locationTag){//한번만 위치를 가져오기 위해서 tag를 주었습니다
            Log.d("myLog"  , "onLocationChanged: !!"  + "onLocationChanged!!");
            double lat =  location.getLatitude();
            double lng = location.getLongitude();

            Toast.makeText(MainActivity.this, "위도  : " + lat +  " 경도: "  + lng ,  Toast.LENGTH_SHORT).show();
            locationTag=false;
        }

    }

    @Override
    public void onProviderDisabled(String provider) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onProviderEnabled(String provider) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
        // TODO Auto-generated method stub

    }
}
AndroidMainfest.xml

    
    
    
    
    

 
 
ui xml


    


+ Recent posts