> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-detect-table-modification.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> 상점,  음식점, 공원, 놀이터, 기념물 등 지도상의 장소 정보가 포함된 1억 건 이상의 레코드로 구성된 데이터셋입니다.

# Foursquare 장소 데이터

export const Image = ({img, alt, size = "lg"}) => {
  const normalizedSize = ["sm", "md", "lg"].includes(size) ? size : "lg";
  return <div className={`ch-image-${normalizedSize}`}>
      <Frame>
        <img src={img} alt={alt} />
      </Frame>
    </div>;
};

<div id="dataset">
  ## 데이터셋
</div>

Foursquare에서 제공하는 이 데이터셋은 [다운로드](https://docs.foursquare.com/data-products/docs/access-fsq-os-places)할 수 있으며
Apache 2.0 라이선스에 따라 무료로 사용할 수 있습니다.

상점, 음식점, 공원, 놀이터, 기념물 등 상업 시설과 관련된 관심 지점(POI) 데이터
1억 건 이상이 포함되어 있습니다. 또한 이러한 장소의 카테고리, 소셜 미디어 정보 등의
추가 메타데이터도 포함되어 있습니다.

<div id="data-exploration">
  ## 데이터 탐색
</div>

데이터를 탐색하기 위해 전체 ClickHouse engine을 제공하는 작은 명령줄 도구인 [`clickhouse-local`](https://clickhouse.com/blog/extracting-converting-querying-local-files-with-sql-clickhouse-local)을 사용하겠습니다.
물론 ClickHouse Cloud, `clickhouse-client`, 또는 `chDB`를 사용할 수도 있습니다.

데이터가 저장된 S3 버킷에서 데이터를 조회하려면 다음 쿼리를 실행하세요:

```sql title="Query" theme={null}
SELECT * FROM s3('s3://fsq-os-places-us-east-1/release/dt=2025-04-08/places/parquet/*') LIMIT 1
```

```response title="Response" theme={null}
Row 1:
──────
fsq_place_id:        4e1ef76cae60cd553dec233f
name:                @VirginAmerica In-flight Via @Gogo
latitude:            37.62120111687914
longitude:           -122.39003793803701
address:             ᴺᵁᴸᴸ
locality:            ᴺᵁᴸᴸ
region:              ᴺᵁᴸᴸ
postcode:            ᴺᵁᴸᴸ
admin_region:        ᴺᵁᴸᴸ
post_town:           ᴺᵁᴸᴸ
po_box:              ᴺᵁᴸᴸ
country:             US
date_created:        2011-07-14
date_refreshed:      2018-07-05
date_closed:         2018-07-05
tel:                 ᴺᵁᴸᴸ
website:             ᴺᵁᴸᴸ
email:               ᴺᵁᴸᴸ
facebook_id:         ᴺᵁᴸᴸ
instagram:           ᴺᵁᴸᴸ
twitter:             ᴺᵁᴸᴸ
fsq_category_ids:    ['4bf58dd8d48988d1f7931735']
fsq_category_labels: ['Travel and Transportation > Transport Hub > Airport > Plane']
placemaker_url:      https://foursquare.com/placemakers/review-place/4e1ef76cae60cd553dec233f
geom:                �^��a�^@Bσ���
bbox:                (-122.39003793803701,37.62120111687914,-122.39003793803701,37.62120111687914)
```

꽤 많은 필드가 `ᴺᵁᴸᴸ`이므로, 좀 더 유용한 데이터를 반환하도록
쿼리에 몇 가지 조건을 추가할 수 있습니다:

```sql title="Query" theme={null}
SELECT * FROM s3('s3://fsq-os-places-us-east-1/release/dt=2025-04-08/places/parquet/*')
   WHERE address IS NOT NULL AND postcode IS NOT NULL AND instagram IS NOT NULL LIMIT 1
```

```response theme={null}
Row 1:
──────
fsq_place_id:        59b2c754b54618784f259654
name:                Villa 722
latitude:            ᴺᵁᴸᴸ
longitude:           ᴺᵁᴸᴸ
address:             Gijzenveldstraat 75
locality:            Zutendaal
region:              Limburg
postcode:            3690
admin_region:        ᴺᵁᴸᴸ
post_town:           ᴺᵁᴸᴸ
po_box:              ᴺᵁᴸᴸ
country:             ᴺᵁᴸᴸ
date_created:        2017-09-08
date_refreshed:      2020-01-25
date_closed:         ᴺᵁᴸᴸ
tel:                 ᴺᵁᴸᴸ
website:             https://www.landal.be
email:               ᴺᵁᴸᴸ
facebook_id:         522698844570949 -- 5,227조
instagram:           landalmooizutendaal
twitter:             landalzdl
fsq_category_ids:    ['56aa371be4b08b9a8d5734e1']
fsq_category_labels: ['Travel and Transportation > Lodging > Vacation Rental']
placemaker_url:      https://foursquare.com/placemakers/review-place/59b2c754b54618784f259654
geom:                ᴺᵁᴸᴸ
bbox:                (NULL,NULL,NULL,NULL)
```

다음 쿼리를 실행하여 `DESCRIBE`로 데이터에서 자동 추론된 스키마를 확인하세요:

```sql title="Query" theme={null}
DESCRIBE s3('s3://fsq-os-places-us-east-1/release/dt=2025-04-08/places/parquet/*')
```

```response title="Response" theme={null}
    ┌─name────────────────┬─type────────────────────────┬
 1. │ fsq_place_id        │ Nullable(String)            │
 2. │ name                │ Nullable(String)            │
 3. │ latitude            │ Nullable(Float64)           │
 4. │ longitude           │ Nullable(Float64)           │
 5. │ address             │ Nullable(String)            │
 6. │ locality            │ Nullable(String)            │
 7. │ region              │ Nullable(String)            │
 8. │ postcode            │ Nullable(String)            │
 9. │ admin_region        │ Nullable(String)            │
10. │ post_town           │ Nullable(String)            │
11. │ po_box              │ Nullable(String)            │
12. │ country             │ Nullable(String)            │
13. │ date_created        │ Nullable(String)            │
14. │ date_refreshed      │ Nullable(String)            │
15. │ date_closed         │ Nullable(String)            │
16. │ tel                 │ Nullable(String)            │
17. │ website             │ Nullable(String)            │
18. │ email               │ Nullable(String)            │
19. │ facebook_id         │ Nullable(Int64)             │
20. │ instagram           │ Nullable(String)            │
21. │ twitter             │ Nullable(String)            │
22. │ fsq_category_ids    │ Array(Nullable(String))     │
23. │ fsq_category_labels │ Array(Nullable(String))     │
24. │ placemaker_url      │ Nullable(String)            │
25. │ geom                │ Nullable(String)            │
26. │ bbox                │ Tuple(                     ↴│
    │                     │↳    xmin Nullable(Float64),↴│
    │                     │↳    ymin Nullable(Float64),↴│
    │                     │↳    xmax Nullable(Float64),↴│
    │                     │↳    ymax Nullable(Float64)) │
    └─────────────────────┴─────────────────────────────┘
```

<div id="loading-the-data">
  ## ClickHouse에 데이터 로드하기
</div>

데이터를 디스크에 영구 저장하려면 `clickhouse-server`
또는 ClickHouse Cloud를 사용할 수 있습니다.

테이블을 생성하려면 다음 명령을 실행하세요:

```sql title="Query" theme={null}
CREATE TABLE foursquare_mercator
(
    fsq_place_id String,
    name String,
    latitude Float64,
    longitude Float64,
    address String,
    locality String,
    region LowCardinality(String),
    postcode LowCardinality(String),
    admin_region LowCardinality(String),
    post_town LowCardinality(String),
    po_box LowCardinality(String),
    country LowCardinality(String),
    date_created Nullable(Date),
    date_refreshed Nullable(Date),
    date_closed Nullable(Date),
    tel String,
    website String,
    email String,
    facebook_id String,
    instagram String,
    twitter String,
    fsq_category_ids Array(String),
    fsq_category_labels Array(String),
    placemaker_url String,
    geom String,
    bbox Tuple(
        xmin Nullable(Float64),
        ymin Nullable(Float64),
        xmax Nullable(Float64),
        ymax Nullable(Float64)
    ),
    category LowCardinality(String) ALIAS fsq_category_labels[1],
    mercator_x UInt32 MATERIALIZED 0xFFFFFFFF * ((longitude + 180) / 360),
    mercator_y UInt32 MATERIALIZED 0xFFFFFFFF * ((1 / 2) - ((log(tan(((latitude + 90) / 360) * pi())) / 2) / pi())),
    INDEX idx_x mercator_x TYPE minmax,
    INDEX idx_y mercator_y TYPE minmax
)
ORDER BY mortonEncode(mercator_x, mercator_y)
```

[`LowCardinality`](/ko/reference/data-types/lowcardinality) 데이터 타입이 여러 컬럼에 사용된 점에 주목하십시오. 이렇게 하면
데이터 타입의 내부 표현이 딕셔너리 인코딩 방식으로 바뀝니다. 딕셔너리 인코딩된 데이터로 작업하면
많은 애플리케이션에서 `SELECT` 쿼리 성능이 크게 향상됩니다.

또한, 지도를 타일로 더 쉽게 분할할 수 있도록 `lat/lon` 좌표를 [Web Mercator projection](https://en.wikipedia.org/wiki/Web_Mercator_projection)에 매핑하는
두 개의 `UInt32` `MATERIALIZED` 컬럼인 `mercator_x`와 `mercator_y`를 생성합니다:

```sql theme={null}
mercator_x UInt32 MATERIALIZED 0xFFFFFFFF * ((longitude + 180) / 360),
mercator_y UInt32 MATERIALIZED 0xFFFFFFFF * ((1 / 2) - ((log(tan(((latitude + 90) / 360) * pi())) / 2) / pi())),
```

위에서 각 컬럼에서 어떤 일이 일어나는지 살펴보겠습니다.

**mercator\_x**

이 컬럼은 경도 값을 메르카토르 도법의 X 좌표로 변환합니다:

* `longitude + 180`은 경도 범위를 \[-180, 180]에서 \[0, 360]으로 이동시킵니다
* 360으로 나누면 값을 0과 1 사이로 정규화합니다
* `0xFFFFFFFF`(32비트 부호 없는 최대 정수의 16진수 표현)를 곱하면 이 정규화된 값을 32비트 정수의 전체 범위로 확장합니다

**mercator\_y**

이 컬럼은 위도 값을 메르카토르 도법의 Y 좌표로 변환합니다:

* `latitude + 90`은 위도 범위를 \[-90, 90]에서 \[0, 180]으로 이동시킵니다
* 360으로 나눈 뒤 pi()를 곱하면 삼각 함수에 사용할 라디안으로 변환됩니다
* `log(tan(...))` 부분은 메르카토르 도법 공식의 핵심입니다
* `0xFFFFFFFF`를 곱하면 32비트 정수의 전체 범위로 확장합니다

`MATERIALIZED`를 지정하면 ClickHouse가 데이터를 `INSERT`할 때 이러한
컬럼의 값을 계산하므로, 원본 데이터 스키마에 포함되지 않은 이 컬럼들을
`INSERT statement`에서 별도로 지정할 필요가 없습니다.

테이블은 `mortonEncode(mercator_x, mercator_y)`를 기준으로 정렬되며, 이는
지리공간 쿼리 성능을 크게 향상시키기 위해 `mercator_x`, `mercator_y`에 대한
Z-order 공간 충전 곡선을 생성합니다. 이 Z-order 곡선 정렬은 데이터가
공간적 인접성을 기준으로 물리적으로 구성되도록 합니다:

```sql theme={null}
ORDER BY mortonEncode(mercator_x, mercator_y)
```

검색 속도를 높이기 위해 `minmax` 인덱스 2개도 생성됩니다:

```sql theme={null}
INDEX idx_x mercator_x TYPE minmax,
INDEX idx_y mercator_y TYPE minmax
```

보시는 것처럼 ClickHouse에는 실시간
매핑 애플리케이션에 필요한 모든 기능이 갖춰져 있습니다!

데이터를 불러오려면 다음 쿼리를 실행하세요:

```sql theme={null}
INSERT INTO foursquare_mercator 
SELECT * FROM s3('s3://fsq-os-places-us-east-1/release/dt=2025-04-08/places/parquet/*')
```

<div id="data-visualization">
  ## 데이터 시각화
</div>

이 데이터셋으로 무엇을 할 수 있는지 보려면 [adsb.exposed](https://adsb.exposed/?dataset=Places\&zoom=5\&lat=52.3488\&lng=4.9219)를 확인해 보세요.
adsb.exposed는 원래 공동 창립자이자 CTO인 Alexey Milovidov가 ADS-B(Automatic Dependent Surveillance-Broadcast)
비행 데이터를 시각화하기 위해 만들었으며, 이 데이터는 규모가 1000배 더 큽니다. 이후 사내 해커톤에서 Alexey가 이 도구에 Foursquare 데이터를 추가했습니다.

특히 인상적인 시각화 몇 가지를 아래에 소개합니다.

<Image img="https://mintcdn.com/private-7c7dfe99-detect-table-modification/bx6sZ_fx0ABo_aFe/images/getting-started/example-datasets/visualization_1.webp?fit=max&auto=format&n=bx6sZ_fx0ABo_aFe&q=85&s=e38a139579a9c1a887ca21ec0577801d" size="md" alt="유럽 내 관심 지점의 밀도 지도" width="2251" height="1509" data-path="images/getting-started/example-datasets/visualization_1.webp" />

<Image img="https://mintcdn.com/private-7c7dfe99-detect-table-modification/bx6sZ_fx0ABo_aFe/images/getting-started/example-datasets/visualization_2.webp?fit=max&auto=format&n=bx6sZ_fx0ABo_aFe&q=85&s=d600f72611f7045737d33a62ae88c6ba" size="md" alt="일본의 사케 바" width="2381" height="1585" data-path="images/getting-started/example-datasets/visualization_2.webp" />

<Image img="https://mintcdn.com/private-7c7dfe99-detect-table-modification/bx6sZ_fx0ABo_aFe/images/getting-started/example-datasets/visualization_3.webp?fit=max&auto=format&n=bx6sZ_fx0ABo_aFe&q=85&s=c6ba45878a077e75c74067321fab7f26" size="md" alt="ATM" width="2130" height="1565" data-path="images/getting-started/example-datasets/visualization_3.webp" />

<Image img="https://mintcdn.com/private-7c7dfe99-detect-table-modification/bx6sZ_fx0ABo_aFe/images/getting-started/example-datasets/visualization_4.webp?fit=max&auto=format&n=bx6sZ_fx0ABo_aFe&q=85&s=8bc5070765ed1e8304b54ebbe5ceced1" size="md" alt="국가별로 분류된 관심 지점이 표시된 유럽 지도" width="633" height="583" data-path="images/getting-started/example-datasets/visualization_4.webp" />
