> ## 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 许可证免费使用。

它包含超过 1 亿条商业兴趣点 (POI) 记录，
例如商店、餐厅、公园、游乐场和纪念碑。它还包含
这些地点的额外元数据，例如类别和社交媒体
信息。

<div id="data-exploration">
  ## 数据探索
</div>

为了探索数据，我们将使用 [`clickhouse-local`](https://clickhouse.com/blog/extracting-converting-querying-local-files-with-sql-clickhouse-local) 这一小型命令行工具，
它提供了完整的 ClickHouse 引擎，不过你也可以使用
ClickHouse Cloud、`clickhouse-client`，甚至 `chDB`。

运行以下查询，从存放数据的 S3 bucket 中查询数据：

```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 -- 522.70 trillion
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` 查看自动推断的数据 schema：

```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`](/zh/reference/data-types/lowcardinality)
数据类型，这会将这些数据类型的内部表示
改为字典编码。对字典编码的数据进行操作，可显著
提升许多应用中 `SELECT` 查询的性能。

此外，还会创建两个 `UInt32` `MATERIALIZED` 列 `mercator_x` 和 `mercator_y`，
用于将经纬度坐标映射到 [Web Mercator projection](https://en.wikipedia.org/wiki/Web_Mercator_projection)，
以便更轻松地将地图划分为多个瓦片：

```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 位无符号整数最大值) ，将这个归一化后的值缩放到 32 位整数的完整范围

**mercator\_y**

这一列会将纬度值转换为墨卡托投影中的 Y 坐标：

* `latitude + 90` 将纬度范围从 \[-90, 90] 平移到 \[0, 180]
* 除以 360 并乘以 pi()，将其转换为三角函数所需的弧度值
* `log(tan(...))` 这一部分是墨卡托投影公式的核心
* 乘以 `0xFFFFFFFF` 会将其缩放到完整的 32 位整数范围

指定 `MATERIALIZED` 可确保 ClickHouse 在我们 `INSERT` 数据时为这些
列计算好值，而无需在 `INSERT` statement 中显式指定这些列 (因为它们并不属于
原始 data schema) 。

该表按 `mortonEncode(mercator_x, mercator_y)` 排序，这会生成
`mercator_x`、`mercator_y` 的 Z-order 空间填充曲线，从而显著
提升地理空间查询性能。这种 Z-order 曲线排序可确保数据
按空间邻近关系进行物理组织：

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

为加快搜索速度，还会创建两个 `minmax` 索引：

```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" />
