Windows Mobile

위치 인식 응용 프로그램에 GPS와 웹 지도 사용

Christopher Mitchell

코드는 MSDN 코드 갤러리에서 다운로드할 수 있습니다.
온라인으로 코드 찾아보기

이 기사에서는 다음 내용에 대해 설명합니다.

  • MapPoint 웹 서비스
  • 작업 및 지도 캐싱
  • 근처의 안내 표시 찾기
  • 새 작업 만들기
이 기사에서 사용하는 기술:
Windows Mobile 6, MapPoint

목차

위치에 MapPoint 사용
작업 및 Pocket Outlook
응용 프로그램 아키텍처
근처 지점 찾기
작업 추가
위치 인식의 활용

최근 필자는 새 집으로 이사를 했는데, 친구 몇 명이 와서 짐을 싸고 풀고, 전기 배선을 정리하고, 그 밖에 중요하지만 지겨운 이사와 관련된 많은 작업을 종일 도왔습니다. 전에 살던 집에서 마지막 짐을 싣고 오는 길에 필자는 친구들과 먹을 저녁거리를 사야 한다는 것을 잊을 뻔했습니다. Windows Mobile 전화기에 알림 기능을 설정했지만 포장 판매가 가능한 가까운 음식점을 지나칠 때 이 알림이 동작하지 않았기 때문입니다.

필자에게 필요했던 것은 정해진 작업을 완수할 수 있는 장소가 가까워질 경우 이를 알려 주는 전화기였습니다. 적절한 시간에 적절한 위치에서 미리 알림 기능이 동작했다면 종일 짐을 옮기느라 지친 몸으로 낯선 동네를 헤매는 대신 저녁을 싸들고 와서 즐겁게 먹을 수 있었겠지요.

Windows Mobile은 주변 환경에 대한 정보, 즉 현재 위치, 통화 가능 구역인지 여부, 신호 강도 등을 장치에서 지속적으로 받을 수 있도록 하는 다양한 인터페이스와 기능을 제공합니다. 그런데 응용 프로그램에서 이러한 기능을 이용하려면 어떻게 해야 할까요? 가장 대표적이면서 유용한 기능 중 하나는 위치 인식입니다. 이 기능을 사용하면 비교적 단순한 위성 내비게이션 프로그램부터 여기에서 설명할, 훨씬 더 정교한 작업 목록에 이르기까지 다양한 위치 인식 응용 프로그램을 만들 수 있습니다.

이번 기사에서는 이 기능을 사용하는 데 관련된 문제, 그리고 유용한 응용 프로그램을 개발하기 위해 추가로 필요한 코드에 대해 살펴보고 모바일 응용 프로그램 개발 환경과 유틸리티에 대해 설명하고, 적절한 시점과 장소에서 작업을 미리 알려 주는 위치 인식 작업 목록 응용 프로그램을 작성하는 방법을 알아보겠습니다.

위치에 MapPoint 사용

wheretodo로 명명된 필자의 위치 인식 작업 목록 응용 프로그램은 몇 가지 중요한 작업을 수행해야 합니다. 우선 현재 전화기의 지리적 위치에 대한 정보를 가져와야 합니다. 또한 작업을 저장하고 모니터링도 해야 합니다. 아울러 현재 작업을 해결하는 데 이용할 수 있는 인근의 상점과 서비스도 알아야 합니다. 마지막으로 전화기에 알림을 제공해야 합니다. 이 응용 프로그램의 인터페이스는 그림 1에서 볼 수 있습니다.

fig01.gif

그림 1 wheretodo 응용 프로그램

전화기에 우선 필요한 것은 지리 데이터입니다. 이 기사에서는 설명을 위해 Microsoft MapPoint 웹 서비스를 사용합니다. 이 웹 서비스는 Live Search MapsVirtual Earth의 기반 기술이며 유럽과 미국에서 인근 서비스를 검색하는 기능을 제공합니다. 제공된 예제 코드는 유럽 지도 데이터 설정을 사용합니다. 다른 곳에서 사용하려면 이 설정을 바꿔야 합니다.

MapPoint는 GPS 위도와 경도, 특정 상점 유형의 검색 코드를 인수로 받습니다. 위치 정보는 FakeGPS 유틸리티를 에뮬레이터 또는 스마트폰에 설치하면 얻을 수 있습니다. (이 유틸리티에 대한 자세한 내용은 "FakeGPS" 보충 기사를 참조하십시오.)

MapPoint는 XML 웹 서비스에 SOAP API를 제공합니다. 이 웹 서비스는 공통 서비스, 찾기 서비스, 렌더링 서비스, 경로 서비스의 네 가지 기본 서비스로 나뉩니다. 이 응용 프로그램에서는 주로 찾기 서비스에 관심을 두지만 사용자 또는 지도에 안내 기능을 제공하려면 다른 서비스를 사용하여 기능을 확장하면 됩니다.

공통 서비스(CommonServiceSoap)에는 찾기, 경로 및 렌더링 서비스에 공통적인 또는 기본적인 유틸리티 기능인 클래스, 메서드 및 속성이 포함됩니다.

찾기 서비스(FindServiceSoap)를 사용하면 MapPoint 웹 서비스 데이터에서 주소, 지리적 객체, 위도 및 경도 좌표, POI(안내 표시)를 찾을 수 있습니다. 또한 주소를 구문 분석하여 지정된 위도 및 경도에 대한 위치 정보를 반환할 수도 있습니다.

렌더링 서비스(RenderServiceSoap)를 사용하면 경로 및 위치 지도를 그리고, 핀으로 지도에 표시를 하고, 다각형 영역을 그리고, 지도 크기와 지도 보기를 설정하고, 지도의 지점을 선택하고, 지도의 지점 및 다각형에 대한 위치 정보를 수집하고, 렌더링된 지도를 이동하거나 확대/축소할 수 있습니다.

경로 서비스(RouteServiceSoap)는 위치 또는 이동 경로를 기반으로 경로, 운전 방향, 계산된 경로 표시(지도에 경로를 강조 표시하여 렌더링하는 데 사용됨)를 생성하고, 세그먼트 및 경로 선호도를 설정하고, 세그먼트 및 방향의 지도 보기를 생성합니다.

전체 개체 모델 클래스 다이어그램은 MSDN에 있습니다. MapPoint는 지리적 영역 또는 필요한 정보의 유형을 기반으로 다양한 데이터 원본에 찾기, 경로 및 렌더링 서비스에 사용되는 데이터를 저장합니다. MSDN에서 서비스 사용에 대한 모든 MapPoint 기술 문서를 찾을 수 있습니다.

작업 및 Pocket Outlook

POOM(Pocket Outlook Object Model)을 사용하면 Windows Mobile의 작업 및 연락처 응용 프로그램에 메뉴와 기능을 추가하고, 이러한 응용 프로그램에 연결된 항목 및 데이터를 조작할 수 있습니다. 위치 인식 응용 프로그램에 적합한 주요 인터페이스는 IAppointment, ITask, IContact의 세 가지입니다.

IAppointment는 일정 폴더의 약속을 나타냅니다. 약속 개체는 회의, 1회 약속 또는 반복적인 약속이나 회의를 나타낼 수 있습니다.

ITask는 지정된 기간 내에 수행해야 하는 할당된, 위임된 또는 자동 할당된 작업을 나타냅니다. 작업 항목은 작업 폴더에 포함되어 있습니다.

IContact는 연락처 폴더의 연락처를 나타냅니다. IContact의 메서드를 사용하여 연락처를 저장, 삭제, 복제 또는 표시할 수 있습니다. IPOutlookItemCollection 인터페이스를 사용하면 새 연락처를 추가하거나 기존 연락처를 검색할 수 있습니다.

샘플 응용 프로그램에서는 ITask를 사용합니다. (기능적으로 보면 IAppointment도 사용할 수 있지만 이 인터페이스는 응용 프로그램의 당면한 요구 사항에는 그다지 적합하지 않습니다.) POOM은 데스크톱 Outlook 개체 모델과 비슷하며 MSDN 기사 "Pocket Outlook 개체 모델과 Outlook 개체 모델의 차이점"에서 이에 대한 자세한 내용을 볼 수 있습니다.

응용 프로그램 아키텍처

위치 인식 응용 프로그램은 두 개의 기능 체인으로 구성됩니다. 첫 번째는 그림 2에서 볼 수 있듯이 현재 작업과 문제를 모니터링하는 시스템입니다. 작업은 SQL Server Compact 데이터베이스 테이블에 저장됩니다. 또한 geocache라는 다른 테이블 내에 보조 데이터 집합이 저장됩니다. 별도의 테이블을 사용하는 것은 필요한 연결의 양을 제한하는 방법입니다. 이에 대해서는 기사 뒷부분에서 설명하겠습니다.

fig02.gif

그림 2 위치 인식 작업 모니터링 및 알림

근처 지점에 대한 정보는 Microsoft MapPoint 웹 서비스에서 제공합니다. MapPoint 웹 서비스에는 세계 각지에 대한 다양한 데이터 원본이 있으며 이중 일부는 부가적인 기능을 제공합니다. 샘플 응용 프로그램에서 사용하는 데이터 원본은 NavTech.EU입니다. 이 정보가 수집되면 일부 거리 계산을 수행할 수 있으며 해당되는 경우 알림이 표시됩니다.

두 번째 이벤트 체인(그림 3 참조)은 POOM을 통해 Windows Mobile 장치의 Tasks 목록에 작업을 추가합니다. 이 프레임워크는 일정 및 SMS 기능에 대한 액세스도 가능하게 합니다.

fig03.gif

그림 3 wheretodo 응용 프로그램의 작업 추가를 위한 시스템 흐름

Tasks와 Cache는 모니터링 및 알림 이벤트 체인의 첫 번째 부분을 나타냅니다. 작업 관리자는 POOM에서 작업 목록을 가져옵니다. 또한 작업 관리자는 연결이 제한적일 수 있는 장치에서의 웹 서비스 사용과 관련된 문제를 해결하는 geocache를 관리합니다. 장치의 연결 수준은 항상 연결되어 있는 경우부터 가끔 연결되거나 전혀 연결이 되지 않는 경우까지 다양합니다.

이러한 광범위한 연결 수준에 대처하려면 웹 서비스 데이터에 적합한 캐시 정책, 그리고 연결을 최대한 활용하는 간단한 시스템이 필요합니다. 물론 매우 복잡한 시나리오도 있겠지만 이 기사에서는 설명을 위해 간단한 반경 캐시 정책을 설정했습니다. MapPoint 웹 서비스를 대상으로 현재 위치에서 지정된 반경, 예컨대 80km 내에 위치한 포장 판매가 가능한 모든 중국 음식점을 찾는 쿼리를 수행한 경우를 예로 들어 보겠습니다. 어느 방향으로든 40km를 이동하면 새 검색이 수행됩니다. 이는 웹 서비스에 대한 모든 쿼리에는 많은 리소스가 소모된다는 원칙을 기반으로 합니다.

또한 작업을 입력한 지점을 기준으로 이동한 거리가 40km를 넘을 가능성은 높지 않다고 가정합니다. 캐시의 동작을 제어하는 데 사용되는 거리는 사람마다, 그리고 국가마다 다를 가능성이 높기 때문에 거리는 사용자가 직접 입력하도록 디자인되었습니다.

쿼리가 수행된 위치의 로그는 geocachelog 데이터베이스에 저장되며, 웹 서비스에서 중복 정보가 요청되지 않도록 이후 다른 쿼리가 수행되기 전에 이 로그가 확인됩니다. 또한 이 로그는 꽤 많은 정보를 미리 채울 수 있는 기회를 시스템에 제공합니다.

FakeGPS

sidebarfig.gif

FakeGPS 응용 프로그램

Windows Mobile 6 SDK에는 FakeGPS라는 유틸리티 응용 프로그램이 포함되어 있습니다. 이 유틸리티를 사용하면 시뮬레이션된 GPS 데이터를 사용하여 응용 프로그램을 테스트할 수 있습니다. FakeGPS 설치, 테스트 및 사용에 대한 자세한 내용은 MSDN Library 기사 "FakeGPS 유틸리티 사용"을 참조하십시오.

FakeGPS는 미리 기록된 GPS 명령 집합을 읽습니다. GPS 데이터는 원시 GPS 데이터 레코더를 사용하여 실제 GPS 장치에서 가져올 수 있습니다. 이렇게 가져온 파일을 FakeGPS 장치에서 읽어 다시 실행하면서 변경되는 위치 데이터를 시뮬레이션할 수 있습니다. 예를 들어 런던 여행을 기록하여 응용 프로그램을 위한 데이터를 수집한 후 FakeGPS를 통해 이 여행을 다시 수행할 수 있습니다.

근처 지점 찾기

다음 구성 요소는 geocache를 채우는 방법을 정의합니다. 응용 프로그램에서 위치를 찾아야 하는 경우 GetNearByPOI 함수가 호출됩니다(그림 4 참조). 이 코드는 MapPoint 웹 서비스를 쿼리합니다. 여기에 나온 코드는 명료함을 위해 간소화되었으므로 코드 다운로드의 코드와는 다릅니다.

그림 4 근처의 안내 표시 찾기

private void GetNearByPOI(
  string KeyWord, LatLong CurrentPosition) {

  FindServiceSoap findService = new FindServiceSoap();
  FindNearbySpecification findNearBySpec = new FindNearbySpecification();
  findService.Credentials = 
    new System.Net.NetworkCredential(myUserName, myPassword);
  findService.PreAuthenticate = true;

  findNearBySpec.Distance = Convert.ToDouble(inputdistance.Text);
  findNearBySpec.LatLong = new LatLong();
  findNearBySpec.LatLong.Latitude = CurrentPosition.Latitude;
  findNearBySpec.LatLong.Longitude = CurrentPosition.Longitude;

  findNearBySpec.Filter = new FindFilter();
  //findNearBySpec.Filter.EntityTypeName = KeyWord;
  findNearBySpec.Filter.EntityTypeName = "FoodType3"; // SIC CODE
  findNearBySpec.DataSourceName = "NavTech.EU";

  FindResults foundResults;
  foundResults = findService.FindNearby(findNearBySpec);

  string connectionString;
  string fileName = System.IO.Path.GetDirectoryName(
    System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase) + 
    "\\wheretodo.sdf";
  string password = "sa";
  connectionString = string.Format("DataSource=\"{0}\"; Password='{1}'", 
    fileName, password);
  SqlCeConnection cn = new SqlCeConnection(connectionString);
  cn.Open();
  SqlCeCommand cmd;

  //Loop Round and add it to the geocache
  foreach (FindResult fr in foundResults.Results) {
    //This needs to include LongLat in geocache
    string sql = 
      "insert into geocache2 (POI_Name, POI_Address, POI_Tel, POI_Web, " +
      "POI_POST_ZIP, POI_Lat, POI_Long, POI_KeyWord) " +
      "values (@Name, @Address, @Tel, @Web, @POST_ZIP, '" + 
      fr.FoundLocation.LatLong.Latitude.ToString() + 
      "', '" + fr.FoundLocation.LatLong.Longitude.ToString() + "', '" + 
      KeyWord.ToString() + "')";
    cmd = new SqlCeCommand(sql, cn);
    cmd.Parameters.Add("@Name", SqlDbType.NVarChar, 255, "Name").Value = 
      fr.FoundLocation.Entity.Properties[0].Value.ToString();
    cmd.Parameters.Add("@Address", SqlDbType.NVarChar, 255, "Address").Value = 
      fr.FoundLocation.Entity.Properties[1].Value.ToString() + 
      fr.FoundLocation.Entity.Properties[2].Value.ToString();
    cmd.Parameters.Add("@Tel", SqlDbType.NVarChar, 255, "Tel").Value = 
      fr.FoundLocation.Entity.Properties[9].Value.ToString();
    cmd.Parameters.Add("@Web", SqlDbType.NVarChar, 255, "Web").Value = 
      "http://m.live.com";
    cmd.Parameters.Add("@POST_ZIP", SqlDbType.NVarChar, 255, "POST_ZIP").Value = 
      fr.FoundLocation.Entity.Properties[7].Value.ToString();
    cmd.ExecuteNonQuery();

    //Update geocachelog
    sql = "insert into GeoCodeLog (Keyword, Lat, Long ) values ('" + KeyWord + 
      "', '" + fr.FoundLocation.LatLong.Latitude.ToString() + "', '" + 
      fr.FoundLocation.LatLong.Longitude.ToString() + "' )";
    cmd = new SqlCeCommand(sql, cn);
    cmd.ExecuteNonQuery();
  }
}

CurrentPosition.Latitude와 CurrentPosition.Longitude가 호출되는 섹션을 살펴보십시오. 최종적으로 이는 응용 프로그램과 GPS 하드웨어용 장치 드라이버 사이에 위치하는 소프트웨어 계층인 GPS Intermediate Driver에서 파생됩니다. 이 추상화 계층은 응용 프로그램을 한 번 작성한 후 여러 GPS 장치에서 사용할 수 있도록 합니다. GPS Intermediate Driver API는 네이티브 코드 라이브러리를 통해 제공됩니다. Windows Mobile 6 Professional SDK에 포함된 샘플을 사용하면 관리되는 코드에서 이 라이브러리에 액세스할 수 있습니다("관리되는 코드에서 GPS Intermediate Driver 사용" 참조).

현재 위치와 근처의 작업 관련 POI를 확보한 다음에는(여기에서는 Navtech.EU 데이터 원본에 사용된 SIC(Standard Industrial Classification) 코드에 의해 확인됨) 이러한 지점이 현재 위치에서 정확히 얼마나 떨어져 있는지 확인해야 합니다. 필자는 웹 서비스와 GPS(여기에서는 FakeGPS)에서 반환된 위도 및 경도 값을 사용했습니다. 그림 5는 이러한 값을 거리로 변환한 방법을 보여 줍니다.

그림 5. 거리 계산

private double GetLatLongTuppleDistance(
  double Lat1, double Long1, double Lat2, double Long2) {

  //Convert Degress to Radians for Calculations
  double Lat1r = ConvertDegreesToRadians(Lat1);
  double Lat2r = ConvertDegreesToRadians(Lat2);
  double Long1r = ConvertDegreesToRadians(Long1);
  double Long2r = ConvertDegreesToRadians(Long2);

  // Spherical law of cosines formula—ignores the effect of hills
  double R = 6371; // Earth's radius (km)
  double d = Math.Acos(Math.Sin(Lat1r) * Math.Sin(Lat2r) +
    Math.Cos(Lat1r) * Math.Cos(Lat2r) *
    Math.Cos(Long2r—Long1r)) * R;
  //Returns distances in km
  return d;
} 

구면 코사인 법칙(피타고라스 정리를 일반화한 법칙)을 기반으로 하는 구면 삼각 함수는 위도와 경도 쌍을 km 거리로 변환하는 함수를 제공합니다. 물론 이 거리 계산에서는 현재 위치와 찾으려는 위치 사이에 있는 요소들(언덕 등)은 대부분 무시됩니다. 지구는 완벽한 구체가 아니므로 이러한 공식을 사용할 경우 오차가 발생합니다. 마일은 km를 1.609344로 나누어 구하고, 혹시 배를 타고 빵을 사러 나간다면 해리는 km를 1.852로 나누어 구합니다.

이 이벤트 체인의 마지막 부분은 현재 위치를 기준으로 특정 거리 내에서 작업에 대한 솔루션이 발견될 경우 사용자에게 알림을 보내는 것입니다. 이 거리는 이동 방법(차량, 도보, 자전거 등)에 따라 결정되므로 사용자가 입력하도록 남겨 두었습니다.

작업 추가

물론 응용 프로그램에서 위치를 찾으려면 먼저 사용자가 모바일 장치에 작업을 추가해야 합니다. POOM은 Outlook 개체 모델과 동일하지만 모바일 장치의 현실적인 제약에 따라 기능 범위는 축소됩니다.

POOM을 사용하면 간단히 약속, 작업, 연락처 항목을 수정 및 표시하고 이러한 항목이 포함된 폴더를 조작할 수 있습니다. 작업 항목을 만드는 코드는 다음과 같습니다.

OutlookSession outlooksession = new OutlookSession();
Task NewTask = new Task();
NewTask.Body = textBox2.Text.ToString();

string MyString = dateTimePicker2.Value.ToShortDateString() + " " + dateTimePicker1.Text.ToString();
DateTime MyDateTime = new DateTime();
MyDateTime = DateTime.ParseExact(MyString, "M/d/yy h:mm:ss tt", null);
NewTask.DueDate = MyDateTime.ToUniversalTime();
NewTask.Subject = textBox1.Text.ToString();
outlooksession.Tasks.Items.Add(NewTask);

다양한 매개 변수를 변경하여 종료 날짜와 작업 본문 텍스트를 설정한 후 이를 POOM에 추가할 수 있습니다.

이 구성 요소는 wheretodo 응용 프로그램의 기능을 제공하는 데 사용되는 두 구성 요소 체인의 마지막 부분을 구성합니다. 시스템 UI 자체는 매우 간단하며, 모든 관련 정보를 설정하고 응용 프로그램이 검색할 SIC 코드를 확인하기 위한 수단을 제공합니다. 전체 응용 프로그램은 위치 인식 응용 프로그램의 사용을 설명하기 위해 무한 루프로 래핑되어 있습니다. 이 응용 프로그램을 손쉽게 모바일 장치의 백그라운드 서비스로 추가하여 Windows Mobile 6 또는 Windows Mobile 5 환경에 보다 효과적으로, 깔끔하게 통합할 수 있습니다.

모바일 데이터 리소스

모바일 및 데스크톱 응용 프로그램을 위한 공통 코드 작성

Going Places: 융통성 있는 Windows Mobile용 응용 프로그램

Data Points: 모바일 응용 프로그램에서 데이터 액세스

위치 인식의 활용

지금까지 한 가지 유형의 위치 인식 장치와 이러한 응용 프로그램 개발을 쉽게 만들어 주는 도구를 살펴봤습니다. 이 기본적인 위치 인식 작업 목록 개념은 RFID용으로도 손쉽게 확장이 가능합니다.

이 코드에는 원하는 위치 인식 응용 프로그램에 적용하기 위해 개선할 수 있는 부분들이 있습니다. 이러한 개선 가능한 부분은 대다수 휴대폰에서 실행되는 다른 컨텍스트 인식 서비스와 위치 인식이 상호 연계되는 방식에 있습니다. 작업은 사용자의 위치만 알 뿐, 정해진 작업 이외에 사용자가 하려는 일이 무엇인지는 알지 못합니다. 작업을 시간과 위치 모두에 맞추면 특정 시간에 어떤 것에 가까이 위치하게 될 때(예를 들어 퇴근하여 집으로 운전할 때) 이러한 작업이 트리거됩니다. 작업 미리 알림 기능은 10분 후에 사용자가 회의에 참석해야 한다는 것을 알 수 있으며, 더 나아가 중간에 우표를 살 시간은 없다는 것까지도 알 수 있습니다. 회의에 참석하러 가는 길 중간에 우표 판매소가 있다고 해도 말이지요.

이를 위한 사용자 지정 설치 관리자를 만들거나 고유의 새로운 위치 인식 응용 프로그램을 구축하는 데 관심이 있다면 MSDN Magazine 2007년 10월호에 실린 필자의 기사(주변 소음에 맞추어 신호음 볼륨 조절)를 참조하십시오.

이 자리를 빌어 항상 필자에게 도움을 주고 소중한 추억을 만들어 주는 친구 Tom Passey와 그의 아내에게 감사 인사를 전합니다. 행운이 함께 하길 기원합니다.

Christopher Mitchell은 기계 학습 및 음악/소리 신호 처리 분야 박사 학위를 취득하고 Kauffman/NCGE에 몸담았다가 현재는 영국 캠브리지에서 신생 기업을 키우고 있습니다. 영국 캠브리지에 소재한 앵글리아 러스킨 대학의 시간 강사이기도 합니다. Chris에게 문의 사항이 있으면 chris.mitchell@anglia.ac.uk로 연락하십시오.