贾国辉
发布于 2025-02-27 / 518 阅读
2
0

你好,真实世界

在本节中,我们将从开始到结束遍历一个完整的示例。通过以下步骤指导:

  1. 提出一个问题并制定解决方案

  2. 建模

  3. 收集和加载数据

  4. 编写查询

  5. 查看结果

1 提出问题

这是你面临的场景:你需要在高速公路一英里内找到快餐餐厅的数量。

2 建模

创建两个表:高速公路、餐厅

创建两个临时表:高速公路临时表、餐厅临时表

2.0 创建空间拓展

CREATE EXTENSION postgis;
CREATE EXTENSION postgis_topology;
CREATE EXTENSION fuzzystrmatch;
CREATE EXTENSION postgis_tiger_geocoder;

2.1 使用模式

验证数据库基础信息

SELECT postgis_full_version();
SELECT version();

创建模式

CREATE SCHEMA ch01;

2.2 餐厅表

接下来,您需要创建一个查找表,将特许经营代码映射到有意义的名称,如下列表所示。然后,您可以添加要处理的所有特许经营。

创建lu_franchises表,用于存储经营的内容

CREATE TABLE ch01.lu_franchises (id char(3) PRIMARY KEY
 , franchise varchar(30)); -- <1>
​
INSERT INTO ch01.lu_franchises(id, franchise) -- <2>
VALUES 
  ('BKG', 'Burger King'), ('CJR', 'Carl''s Jr'),
  ('HDE', 'Hardee'), ('INO', 'In-N-Out'), 
  ('JIB', 'Jack in the Box'), ('KFC', 'Kentucky Fried Chicken'),
  ('MCD', 'McDonald'), ('PZH', 'Pizza Hut'),
  ('TCB', 'Taco Bell'), ('WDY', 'Wendys');

创建餐厅表restaurants

为了您以后的分析,您需要唯一地标识餐馆,这样您就不会重复计算它们。此外,某些地图服务器和查看器(例如MapServer和QGIS)是没有整数主键和唯一索引的表。餐厅数据没有主键,并且数据文件中没有任何内容适合良好的自然主键,因此您创建一个自动编号主键

CREATE TABLE ch01.restaurants
(
  id serial primary key,   -- <1>
  franchise char(3) NOT NULL,
  geom geometry(point,2163) -- <2>
);

创建空间索引restaurants_geom_idx

接下来,您需要在几何列上放置空间索引。此步骤可以在数据加载之前或之后完成。

CREATE INDEX ix_code_restaurants_geom 
  ON ch01.restaurants USING gist(geom); 

作为 PostgreSQL 中索引定义的一部分,您必须指定索引类型,如前面的 CREATE INDEX 中所述。PostGIS 空间索引属于 gist、sgist 或 brin 索引类型。对于大多数用例,您需要坚持使用 gist。我们将在本书后面介绍何时使用每种索引类型。

-- tag::code_restaurants_fk[] --

ALTER TABLE ch01.restaurants 
  ADD CONSTRAINT fk_restaurants_lu_franchises
  FOREIGN KEY (franchise) 
  REFERENCES ch01.lu_franchises (id)
  ON UPDATE CASCADE ON DELETE RESTRICT;

创建索引_restaurants_fki,使两个表之间的连接更加高效。

CREATE INDEX fi_restaurants_franchises 
 ON ch01.restaurants (franchise);

2.3 高速公路表

创建高速公路数据表highways

CREATE TABLE ch01.highways -- <1>
(
  gid integer NOT NULL,
  feature character varying(80),
  name character varying(120),
  state character varying(2),
  geom geometry(multilinestring,2163), -- <2>
  CONSTRAINT pk_highways PRIMARY KEY (gid)
);

创建索引

CREATE INDEX ix_highways 
 ON ch01.highways USING gist(geom); -- <3>

2.4 创建临时表并导入数据

创建餐厅临时表restaurants_staging

CREATE TABLE ch01.restaurants_staging (
  franchise text, lat double precision, lon double precision);

给临时表导入数据

copy ch01.restaurants_staging FROM 'D:/postgisdata/ch01/data/restaurants.csv' DELIMITER as ',';

根据临时表中的数据更新正式的餐厅表

INSERT INTO ch01.restaurants (franchise, geom)
SELECT franchise
 , ST_Transform(
   ST_SetSRID(ST_Point(lon , lat), 4326)
   , 2163) As geom
FROM ch01.restaurants_staging; 

创建高速公路临时表,并同步导入数据

shp2pgsql -D -s 4269:2163 -g geom -I D:/postgisdata/ch01/data/roadtrl020.shp ch01.highways_staging | psql -h localhost -U postgres -p 5432 -d postgres

根据高速公路临时表导入正式的高速数据highways

INSERT INTO ch01.highways (gid, feature, name, state, geom)
SELECT gid, feature, name, state, ST_Transform(geom, 2163)
FROM ch01.highways_staging
WHERE feature LIKE 'Principal Highway%';

3 编写查询代码

问题:高速公路1英里范围内有多少家快餐店?

SELECT f.franchise
 , COUNT(DISTINCT r.id) As total -- <1>
FROM ch01.restaurants As r 
  INNER JOIN ch01.lu_franchises As f ON r.franchise = f.id
    INNER JOIN ch01.highways As h 
      ON ST_DWithin(r.geom, h.geom, 1609) -- <2>
GROUP BY f.franchise
ORDER BY total DESC;

该示例的关键在于,您可以使用ST_DWithin函数将餐厅表与高速公路表连接起来。此常用函数接受两个几何图形,如果两个几何图元之间的距离在指定距离内达到最小值,则该函数为真。在本例中,传入了一个代表餐厅的点和一个代表高速公路的线串集合,距离是1609m。所有符合连接条件的餐厅-高速公路组合将被过滤出来。

注:1英里=1609米

3.1 使用QGIS可视化查询结果

在马里兰州美国1号公路20英里的缓冲区内定位哈迪(Hardee)餐厅,下面是获取餐厅总数的查询语句

SELECT COUNT(DISTINCT r.id) As total
FROM ch01.restaurants As r 
     INNER JOIN ch01.highways As h 
     ON ST_DWithin(r.geom, h.geom, 1609*20)
WHERE r.franchise = 'HDE' 
 AND h.name  = 'US Route 1' AND h.state = 'MD';

3.1.1 美国1号公路可视化

SELECT gid, name, geom 
FROM ch01.highways
WHERE name = 'US Route 1' AND state = 'MD';

3.1.2 叠加20英里的缓冲区

SELECT ST_Union(ST_Buffer(geom, 1609*20))
FROM ch01.highways
WHERE name = 'US Route 1' AND state = 'MD';

3.1.3 将哈迪餐厅定位在缓冲区路径上

SELECT r.geom
FROM ch01.restaurants r
WHERE EXISTS 
 (SELECT gid FROM ch01.highways 
  WHERE ST_DWithin(r.geom, geom, 1609*20) AND 
  name = 'US Route 1' 
   AND state = 'MD' AND r.franchise = 'HDE');

4 作业

分析本溪市哪些旅游景点位于道路编码为G304的20KM范围内,并用地图符号表达。


评论