새로운 기능: 사진 탐색기 · 지도에 표시된 모든 여행 사진을 찾기

엔지니어링 저널

Mapsake가 오프라인 여행 지리 정보를 구축하는 방법.

사진 좌표를 국가, 지역, 도시 및 공항으로 변환하려면 가장 가까운 장소 검색만으로는 충분하지 않습니다. Mapsake는 공개 장소 데이터, 경계 지오메트리, 결정론적 규칙 및 로컬 인덱스를 결합하여 이를 수행합니다.

에 의해. 12 분 읽기
묶인 지리적 경계를 기반으로 구축된 상세한 세계 지도를 보여주는 Mapsake 지도

좌표는 아직 장소가 아닙니다.

여행 사진에는 놀라울 정도로 정확한 위도와 경도가 포함될 수 있습니다. 하지만 이 두 개의 숫자만으로는 카메라가 일본, 교토, 교토시, 또는 집으로 돌아가는 길에 간사이 국제공항에 있었는지 알 수 없습니다. Mapsake는 이러한 계층 구조를 필요로 합니다. 그렇지 않으면, 위치 정보는 유용한 여행 기록이 될 수 없습니다.

이러한 질문에 답하는 구성 요소는 지리적 명명된 장소의 구조화된 디렉토리인 지리 Gazetteer입니다. Mapsake는 국가 및 1차 지역 경계를 그리는 데 사용되는 경계선과 함께 Gazetteer를 앱 내에 포함합니다. 검색, 수동 마킹, 사진 가져오기, 장소 스토리, 여권 통계, 업적, 심지어 친구 뷰까지 이 기능에 의존합니다.

모든 좌표를 웹 지오코더에 보내는 것이 더 간단했을 수 있습니다. 하지만 이는 대량의 사진 가져오기를 더 느리게 만들고, 네트워크 의존적으로 만들며, 재현하기 어렵게 만들고, 개인 정보 보호를 약화시킵니다. 오프라인 경로는 더 많은 초기 엔지니어링이 필요했지만, Mapsake는 항공기, 집, 그리고 소스 데이터 세트가 변경된 수년 후에도 동일하게 작동하는 안정적인 지리적 어휘를 제공할 수 있게 했습니다.

이 레이어가 어떻게 구축되었는지, 점과 선이 일치하지 않는 경우, 그리고 "가장 가까운 도시"가 올바른 답변의 시작일 뿐이라는 이유를 설명합니다.

4개의 공개 데이터 세트, 4가지 다른 작업

Mapsake에 필요한 모든 정보를 단일 소스에서 얻을 수 없으므로, 빌드 파이프라인은 네 가지 종류의 공개 데이터를 결합합니다.

  • GeoNames 국가, 1차 행정 구역, 인구 있는 지역, 안정적인 식별자, 다른 이름, 좌표, 그리고 인구를 제공합니다.
  • OurAirports 공항, IATA 및 ICAO 코드, 이름, 시, 그리고 좌표를 제공합니다.
  • Natural Earth 국가 및 admin-1 경계 지오메트리와 유용한 지도 라벨 지점을 제공합니다.
  • Mapsake가 소유한 작은 레이어는 지원되는 국가별 모드 수, 별칭, 그리고 일반적인 소스로 안전하게 추론할 수 없는 제품 결정 사항을 기록합니다.

각 데이터 소스는 서로 다른 기능을 제공합니다. GeoNames은 도시가 특정 지역 및 국가에 속한다는 것을 알고 있지만, 도시 좌표는 점이며 경계선이 아닙니다. Natural Earth은 다각형의 위치를 알려주지만, 기능 식별자가 항상 GeoNames의 식별자와 일치하지는 않습니다. OurAirports는 KLAX와 LAX가 동일한 공항을 나타낸다는 것을 알고 있지만, 공항 주변의 모든 장소에 대한 계층 구조는 아닙니다.

빌드 스크립트는 업스트림 파일을 다운로드하고 캐시하며, 정규화하고, 관계를 검증하고, 두 개의 커밋된 아티팩트를 생성합니다. 여기에는 읽기 전용 SQLite 데이터베이스와 단순화된 GeoJSON 지오메트리가 포함됩니다. 앱은 이러한 결과를 배포합니다. 앱은 초기 실행 시 월드 데이터베이스를 다운로드하지 않으며, 여행 중에 소스 사이트가 온라인 상태인지에 의존하지 않습니다.

생성된 결과물을 저장하면 재현성을 확보할 수 있습니다. 배포된 버전은 알려진 지리 정보를 포함합니다. 소스 코드 업데이트는 테스트 및 검토가 가능한 의도적인 코드 변경이며, 사용자에게 영향을 미치는 서버 측 변경이 아닙니다.

의도적으로 단순한 SQLite 데이터베이스입니다.

첫 번째 지리 정보 데이터베이스에는 252개 국가, 3,861개 지역, 33,744개 도시, 그리고 4,564개 공항이 포함되어 있으며, 약 14 MB의 크기였습니다. 이는 운영 체제에서 이미 제공하는 SQLite 라이브러리와 작은 로컬 래퍼를 사용했으며, 더 큰 데이터베이스 프레임워크를 추가하지 않았습니다.

이 스키마는 의도적으로 간단합니다. 대륙은 국가를 포함하고, 국가는 지역을 포함하고, 지역은 도시를 포함합니다. 공항에는 국가 코드와 좌표가 포함됩니다. 안정적인 소스 식별자는 Mapsake 마크에 저장되는 식별자가 됩니다. 국가에는 ISO alpha-2, 지역에는 GeoNames 관리 코드, 도시에는 GeoNames ID, 공항에는 IATA 코드가 사용됩니다.

Mapsake는 읽기 쉬운 이름과 계통을 각 저장된 위치에 중복 저장합니다. 이 중복은 유용합니다. 개인 여행 기록은 나중에 Gazetteer가 장소 이름을 변경하거나, 기록을 삭제하거나, 존재하지 않을 경우에도 읽을 수 있어야 합니다. 식별자는 일치에 사용되며, 스냅샷은 사용자 데이터를 자체적으로 유지합니다.

SQLite는 이 워크로드에 적합합니다. 데이터베이스는 한 번 생성되고 여러 번 쿼리되기 때문입니다. 인덱스, 빌드 중 트랜잭션 및 서비스 프로세스 없이 전체 텍스트 검색을 지원합니다. 앱은 번들 파일만 읽기 전용으로 열기 때문에 참조 데이터에 대한 마이그레이션 위험이 없고, 중단된 쓰기로 인해 데이터가 손상될 가능성도 없습니다.

검색은 단순히 '포함'하는 것 이상의 의미를 가집니다.

수동 마킹은 대륙, 국가, 지역, 도시 및 공항을 포괄하는 단일 검색 필드로 시작됩니다. 'san' 검색은 일반적인 도시가 알려지지 않은 레코드보다 우선적으로 검색되어야 합니다. 'LAX' 검색은 공항을 검색해야 합니다. 또한 악센트 기호가 없는 이름도 검색할 수 있어야 합니다.

이 빌드는 표시 이름, 선택된 대체 이름, 코드, 계통, 좌표, 종류 및 중요도 값을 포함하는 FTS5 테이블을 만듭니다. 유니코드 토크나이저는 일치시키기 위해 구문 부호화를 제거합니다. 쿼리 시 Mapsake는 대소문자를 무시하고, 구문 부호가 될 수 있는 문자를 제거하고, 각 토큰에 접두사 일치를 추가하고, 정확한 이름을 접두사 및 일반 일치보다 우선시합니다.

중요도는 나머지 항목의 순위를 결정하는 데 영향을 미칩니다. 대륙과 국가는 유사한 이름의 마을보다 우선 순위가 높아야 합니다. 도시의 인구는 주요 위치에 적절한 가중치를 부여합니다. 텍스트 일치가 동일한 경우 대형 공항이 소형 공항보다 높은 순위를 갖습니다.

대체 이름은 의도적으로 제한됩니다. GeoNames는 인기 있는 장소에 대해 매우 긴 다국어 목록을 제공할 수 있습니다. 모든 철자를 장치 인덱스에 복사하면 노이즈와 크기가 증가합니다. 빌더는 유용한 변형의 제한된 중복 제거된 세트를 유지하고 원래 표시 이름을 검색 폼으로 접힌 것과 별도로 유지합니다.

검색 결과는 계층적 탐색에 사용되는 GazetteerPlace 모델과 동일한 것을 사용합니다. 사용자는 직접 검색하거나 대륙, 국가, 지역 및 도시를 탐색할 수 있으며, 두 가지 다른 지리 시스템을 만들 필요가 없습니다.

선은 점과는 다른 질문에 답합니다.

첫 번째 사진 해결 기능은 가장 가까운 도시를 선택하고 해당 도시의 국가와 지역을 상속했습니다. 밀집된 지역에서는 이것이 종종 완벽해 보입니다. 국경 근처에서는 알아차리기 어려운 방식으로 잘못될 수 있습니다.

몬타나 주 내부에 위치했지만 가장 가까운 인구 밀집 지역이 데이터베이스에 따르면 노스다코타 주에 있는 사진을 찍은 상황을 상상해 보세요. 가장 가까운 도시 계산은 올바르게 작동하지만 결과는 사진이 찍힌 행정 구역과 일치하지 않습니다. 이와 같은 문제는 국제 국경, 고립 지역, 해안선이 듬성듬성하여 가까운 인구 밀집 지역이 없는 해안 지역에서도 발생합니다.

경계 데이터는 근접성이 아닌 포함 여부를 판단합니다. Mapsake는 Natural Earth 국가 및 admin-1 다각형을 디코딩하고, 좌표가 포함된 링을 확인하고, 해당 결과를 사용하여 국가 및 지역 할당을 보호합니다. 그런 다음, 다각형 국가 또는 지역에 제한된 가장 가까운 도시를 검색할 수 있습니다.

이는 유용한 업무 분담을 가능하게 합니다.

  1. 다각형 포함은 행정 구역을 설정합니다.
  2. Gazetteer의 계층 구조는 안정적인 ID와 이름을 제공합니다.
  3. 제한된 가장 가까운 도시 검색은 설정된 경계를 넘지 않으면서 유용한 지역 정보를 제공합니다.
  4. 근처 공항 확인은 미리 설정된 짧은 거리 내의 공항만 추가합니다.

선 데이터와 장소 디렉터리는 각각으로는 충분하지 않습니다. 함께 사용하면 좌표를 신뢰할 수 있는 경로로 연결합니다.

admin-1 감사에서 체계적인 불일치가 드러났습니다.

국가 폴리곤과 지역 폴리곤은 Natural Earth의 서로 다른 레이어에서 가져왔습니다. 지역 레이어의 식별자는 항상 GeoNames와 일치하지 않았습니다. 일부 오류는 명확했지만, 다른 오류는 타당해 보였지만 부정확한 결과를 초래했습니다.

초기 변환 테이블 중 하나에서 퀘벡에 뉴브런즈윅의 지리 정보가 할당되었습니다. 다른 기능은 코드가 누락되었거나, 인접 시스템의 코드를 사용했거나, Gazetteer와 다른 관리 단위로 표현되었습니다. 세계 지도를 시각적으로 확인하더라도 이러한 오류를 모두 정확하게 찾을 수 없습니다.

새 모델 상태에서는 도시를 오라클로 취급합니다. 각 후보 다각형에 대해 해당 다각형 내에 실제로 어떤 지역의 도시가 포함되어 있는지 묻습니다. 기존 Natural Earth 코드는 맹목적으로 신뢰하는 대신 유효성을 검사합니다. 유효성 검사에 실패한 기능은 공간적으로 재할당하거나 분할하거나 제외할 수 있습니다. 생성된 출력은 SQLite에 이미 존재하는 정확한 지역 ID를 사용합니다.

이는 실질적인 교차 데이터 세트 테스트 방법입니다. 특정 지역이라고 주장하는 다각형은 동일한 지역이라고 주장하는 도시 샘플을 포함해야 합니다. 두 데이터 소스가 일치하지 않는 경우, 시스템은 어떤 값을 먼저 로드했는지 조용히 선택하는 대신 증거를 제공합니다.

동일한 감사에서, 부모 국가의 지리적 영역에 포함된 인접 지역과 같은 특수한 사례가 발견되었습니다. Mapsake는 이러한 사례 중 일부를 수정하여 사진이 가제터 및 국가 수 설정에서 인식하는 국가와 동일한 국가로 연결되도록 합니다.

원본 이미지 크기의 세계 데이터를 사용하지 않고도 선명한 선을 표시합니다.

최초의 Atlas는 Natural Earth의 1:110백만 건의 국가 경계 레이어를 사용했습니다. 작고 빠르기는 했지만, Mapsake가 더 자세한 지도, 장소 이야기, 지역 정보를 추가하면서 해안선이 눈에 띄게 거칠어지는 문제가 발생했습니다.

지도는 1:10백만 레이어로 이동했습니다. 해당 소스는 훨씬 더 자세하므로 변경하지 않고 번들링하고 렌더링하면 저장 공간, 디코딩 시간, 오버레이 구성 및 색상 작업이 증가합니다. 빌드 파이프라인은 Douglas-Peucker 알고리즘을 사용하여 약 0.004도 허용 오차 내에서 각 원을 단순화한 다음 좌표를 안정적인 정밀도로 반올림합니다.

결과 국가 파일은 약 6.5 MB입니다. Mapsake가 표시하는 줌 레벨에서 유용한 해안선 세부 정보를 유지하면서, 실질적으로 동일한 픽셀에 배치되는 정점을 제거합니다. Admin-1 지오메트리도 유사한 유효성 검사 및 단순화 단계를 거칩니다.

단순화에는 정확성 제약이 있습니다. 작은 폴리곤이라도 실제 사진 좌표에 대해 동일한 결정을 내려야 합니다. 이후 벤치마크 테스트에서는 의도적으로 경계 주변에 점을 배치하고 최적화된 충돌 감지와 변경되지 않은 참조 데이터를 비교합니다. 더 빠른 선은 동일한 포함 여부를 판단할 수 있는 경우에만 유용합니다.

34,000 에서 234,000 까지의 도시로 확장합니다.

첫 번째 데이터베이스는 15,000 이상의 인구를 가진 GeoNames 도시를 사용했습니다. 이를 통해 번들 크기를 작게 유지했지만, 농촌 여행, 작은 섬, 트레일 타운, 그리고 많은 집 위치에서 불필요하게 먼 레이블이 표시되었습니다.

Mapsake은 이후 GeoNames을 채택했습니다. 도시500, 는 대략 500 명 이상의 인구를 가진 지역과 행정 중심지를 포함합니다. 도시 테이블은 약 7배 증가하여 약 234,000 개의 레코드가 되었고, 번들 데이터베이스는 약 14 MB에서 약 69 MB로 증가했습니다.

이 거래는 App Clip이 제거된 후에 이루어졌습니다. Clip의 다운로드 제한은 데이터베이스를 제한하는 가장 큰 이유였습니다. 메인 앱만 사용하게 되면서 더 넓은 범위의 커버리지가 인공적인 소도시 제한을 유지하는 것보다 더 가치가 있었습니다.

전체 텍스트 인덱스는 사소한 장소의 대체 이름에 대해 선택적으로 작동하며, 지역별 탐색은 한 번에 표시되는 양을 제한합니다. 데이터는 광범위할 수 있지만, 모든 화면에서 전체 테이블을 표시할 필요는 없습니다.

커버리지는 네트워크 지오코더가 가장 신뢰할 수 없는 위치에서 가장 중요합니다. 멀리 떨어진 작은 마을은 데이터 세트에 포함되지 않았기 때문에 수 시간 거리에 있는 도시로 잘못 표시되어서는 안 됩니다.

가장 가까운 도시 검색이 공유 성능 문제로 이어졌습니다.

원래의 가장 가까운 도시 쿼리는 위도와 경도의 상자를 넓히고, SQLite에 각 후보의 가중 거리를 계산하도록 요청하고, 임시 정렬을 만들고, 가장 가까운 행을 반환했습니다. 이해하기 쉽고 충분히 정확했지만, 82,000 사진 라이브러리는 작은 쿼리 비용을 반복적인 작업으로 인해 수 초로 늘렸습니다 (예: 가져오기, 메모, 지도 생성, Constellations).

첫 번째 개선 사항은 더 넓은 대체 방법을 사용하기 전에 좁은 0.4도 범위를 검색하도록 했습니다. 밀집된 지역에서는 일반적으로 훨씬 작은 후보 집합에서 도시를 찾을 수 있습니다. 메모이제이션과 지속적인 지리적 셀 캐시를 통해 근처 사진이 동일한 작업을 반복하는 것을 방지했습니다.

더 큰 개선 사항은 234,000 도시의 숫자 좌표를 위도별로 정렬된 메모리 내 인덱스에 로드하는 것입니다. 이진 검색을 사용하여 현재 위도 범위 내의 슬라이스를 찾고, 제한된 루프에서 경도와 가중 거리를 확인하고, SQLite에서 일치하는 행만 가져옵니다. 작은 레코드 캐시는 반복적으로 발생하는 결과를 저렴하게 가져오도록 합니다.

동작 방식은 변경되지 않았습니다. 최적화된 함수는 여전히 첫 번째 비어 있지 않은 검색 창에서 가장 가까운 도시를 선택하며, 결정론적인 동점 해결 방식을 사용합니다. 이전 SQL 경로의 벤치마크 전용 복사본은 수백 개의 경계가 많은 좌표에 대해 정확한 ID 일치를 확인합니다.

시뮬레이터에서, 중간 정도의 가장 가까운 도시 일괄 처리 시간이 2.11초에서 5.2밀리초로 단축되었습니다. 실제 iPhone에서는 3.06초에서 6.35밀리초로 단축되었습니다. 이러한 개선 사항은 가제터가 인ポート 화면의 개별 기능이 아닌 공유 인프라스트럭처이기 때문에 여러 기능에 영향을 미쳤습니다.

겹치는 다각형이 발견되었으며, 이는 버그입니다.

성능 동등성 검사를 통해 최적화 전에 존재했던 버그가 발견되었습니다. 일부 admin-1 다각형은 의도적으로 겹쳐지는 경우가 있습니다. 특히 수도권이 주변 지역에 포함되는 경우가 있습니다. 베를린과 브란덴부르크, 서울과 경기도, 그리고 키이우 시와 그 지역이 그 예입니다.

이전 버전의 히트 테스트는 Swift 딕셔너리에서 일치하는 다각형 중 가장 먼저 나타나는 것을 선택했습니다. 딕셔너리의 반복 순서는 프로세스마다 변경될 수 있으므로, 동일한 좌표가 앱을 다시 시작하면 다른 영역을 가리키게 될 수 있습니다.

Mapsake은 이제 겹치는 후보 항목을 다각형 면적으로 정렬하고, 가장 작고 구체적인 항목이 우선하도록 합니다. 참조 구현도 동일한 규칙을 따릅니다. 82,000개의 사진을 사용한 고정 테스트 사례는 콜드 및 웜 파생 경로 모두에서 차이가 0이어야 합니다. 그렇지 않으면 기하학적 최적화는 허용되지 않습니다.

이 버그는 ‘다각형 내부’가 항상 예 또는 아니요로만 답할 수 있는 문제가 아니라는 점을 잘 보여줍니다. 지리 데이터에는 월경지, 다른 행정 구역 안에 놓인 수도, 날짜 변경선 횡단, 멀티폴리곤, 구멍, 분쟁 경계, 데이터 출처별 관례가 포함됩니다. 결정론적 정책은 점-다각형 포함 알고리즘만큼 중요합니다.

오프라인 모드는 개인 정보 보호 기능이자 제품 기능입니다.

Mapsake의 사진 가져오기 기능은 좌표를 타사 지오코딩 서비스로 전송하지 않고도 대량의 사진 라이브러리를 처리할 수 있습니다. 이를 통해 민감한 여행 및 주소 정보가 보호되고, 요청당 요금이 부과되지 않으며, 처리 속도가 예측 가능합니다.

또한, 편집을 더욱 일관성 있게 만듭니다. 수동 검색, 사진 가져오기, Passport 카드, 성취, 친구 스냅샷, 스탬프 매칭 및 장소 스토리는 동일한 안정적인 장소 ID를 사용하므로 동일한 언어를 사용합니다. 항공 로그에서 가져온 공항은 사진 근처에서 감지된 동일한 공항과 중복을 제거할 수 있습니다. 검색을 통해 찾은 도시는 Then & Now에서 사용되는 도시와 일치할 수 있습니다.

번들은 완벽하거나 영구적인 것으로 간주되지 않습니다. 소스 출처는 앱 내에서 표시됩니다. 빌드 스크립트는 코드 옆에 보관됩니다. 알려진 수정 사항은 명시적으로 표시됩니다. 벤치마크 테스트 데이터는 어려운 좌표에서 동작을 보호합니다. 지리적 세계를 업데이트하는 것은 검토 가능한 결과를 초래하는 릴리스 이벤트입니다.

다시 만들 때 무엇을 유지하고 싶습니까?

가장 견고한 선택 사항은 개별 알고리즘이 아니었습니다. 책임 경계였습니다.

  • 이름, 안정적인 식별자, 검색 및 계층 구조를 위해 점을 사용합니다.
  • 포함을 위해 선과 다각형을 사용합니다.
  • 휴대폰에서 4가지 형식을 파싱하는 대신, 읽기 전용 제품 아티팩트를 생성합니다.
  • 사용자 데이터를 포함하는 읽기 쉬운 스냅샷을 저장하고, 원본 ID를 유지하여 매칭을 가능하게 합니다.
  • 속도를 높이기 전에 일반적인 오프라인 경로를 결정적으로 만듭니다.
  • 최적화된 출력이 동일하다는 것을 증명할 때까지, 참조 구현을 충분히 오랫동안 유지하세요.

Gazetteer는 처음에는 검색 기능으로 시작되었습니다. 하지만 Mapsake의 핵심 시스템 중 하나가 되었습니다. 왜냐하면, 대부분의 고급 기능은 동일한 기본적인 질문에 답해야 하기 때문입니다. 바로 "이것은 어떤 장소인가?"

올바른 답변을 얻으려면 지리학이 단일 데이터베이스나 정교한 쿼리가 아니라는 점을 이해해야 합니다. 그것은 이름, 위치, 선, 제품 규칙, 그리고 사용자가 인식할 것으로 예상하는 여행 기록 간의 신중한 합의입니다.

Mapsake 앱 아이콘

Mapsake의 독립 개발자가 작성했으며, 앱 뒤에 있는 제품, 지도, 개인 정보 보호 및 Apple 플랫폼 작업에 대한 정보를 제공합니다.

나만의 여행 지도를 만드세요.

이미 가지고 있는 여행 기록부터 시작하세요.

Mapsake은 무료이며, 핵심 기능 사용을 위해 Mapsake 계정이 필요하지 않으며, 지원되는 사진 매칭 기능은 장치 내에서 작동합니다.

Mapsake 무료 다운로드