在本节中,我们将从开始到结束遍历一个完整的示例。通过以下步骤指导:
提出一个问题并制定解决方案
建模
收集和加载数据
编写查询
查看结果
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范围内,并用地图符号表达。