16.5. الدرس: إنشاء الأشكال الهندسية¶
سنتعمق في هذا القسم قليلًا في كيفية إنشاء الأشكال الهندسية البسيطة باستخدام SQL. في الواقع، ستستخدم على الأرجح نظام معلومات جغرافية مثل QGIS لإنشاء أشكال هندسية معقدة باستخدام أدوات الرقمنة فيه؛ لكن فهم كيفية صياغتها قد يكون مفيدًا لكتابة الاستعلامات وفهم كيفية بناء قاعدة البيانات.
هدف هذا الدرس: فهم أفضل لكيفية إنشاء الكيانات المكانية مباشرة في PostgreSQL.
16.5.1. إنشاء الخطوط (Linestrings)¶
بالعودة إلى قاعدة البيانات address، لنجعل جدول الشوارع مطابقًا للجداول الأخرى؛ أي أن يكون له قيد على الشكل الهندسي، وفهرس، وإدخال في الجدول geometry_columns.
16.5.2. جرّب بنفسك: ★★☆¶
عدّل الجدول
streetsبحيث يحتوي على عمود شكل هندسي من النوع ST_LineString.لا تنسَ إجراء التحديث المصاحب على جدول أعمدة الأشكال الهندسية!
أضف أيضًا قيدًا يمنع إضافة أي أشكال هندسية ليست من النوع LINESTRING أو فارغة (null).
أنشئ فهرسًا مكانيًا على عمود الشكل الهندسي الجديد
الإجابة
alter table streets add column geom geometry;
alter table streets add constraint streets_geom_point_chk check
(st_geometrytype(geom) = 'ST_LineString'::text OR geom IS NULL);
insert into geometry_columns values ('','public','streets','geom',2,4326,
'LINESTRING');
create index streets_geo_idx
on streets
using gist
(geom);
لنُدرج الآن خطًا في جدول الشوارع. في هذه الحالة سنحدّث سجل شارع موجود:
update streets
set geom = 'SRID=4326;LINESTRING(20 -33, 21 -34, 24 -33)'
where streets.id=2;
ألقِ نظرة على النتائج في QGIS. (قد تحتاج إلى النقر بزر الفأرة الأيمن على طبقة الشوارع في لوحة 'Layers'، واختيار 'Zoom to layer extent'.)
أنشئ الآن مزيدًا من إدخالات الشوارع، بعضها في QGIS وبعضها من سطر الأوامر.
16.5.3. إنشاء المضلعات¶
إنشاء المضلعات سهل بالقدر نفسه. ومما يجب تذكّره أن المضلعات، بحكم تعريفها، تحتوي على أربعة رؤوس على الأقل، يكون الأخير والأول منها في الموقع نفسه:
insert into cities (name, geom)
values ('Tokyo', 'SRID=4326;POLYGON((10 -10, 5 -32, 30 -27, 10 -10))');
ملاحظة
يتطلب المضلع أقواسًا مزدوجة حول قائمة إحداثياته؛ وذلك لتمكينك من إضافة مضلعات معقدة ذات مناطق متعددة غير متصلة. على سبيل المثال
insert into cities (name, geom)
values ('Tokyo Outer Wards',
'SRID=4326;POLYGON((20 10, 20 20, 35 20, 20 10),
(-10 -30, -5 0, -15 -15, -10 -30))'
);
إذا اتبعت هذه الخطوة، يمكنك التحقق مما فعلته بتحميل مجموعة بيانات المدن في QGIS، وفتح جدول بياناتها الوصفية، وتحديد الإدخال الجديد. لاحظ كيف يتصرف المضلعان الجديدان كمضلع واحد.
16.5.4. تمرين: ربط المدن بالأشخاص¶
في هذا التمرين عليك القيام بما يلي:
احذف جميع البيانات من جدول الأشخاص.
أضف إلى جدول الأشخاص عمود مفتاح أجنبي يشير إلى المفتاح الأساسي لجدول المدن.
استخدم QGIS لالتقاط (رسم) بعض المدن.
استخدم SQL لإدراج بعض سجلات الأشخاص الجديدة، مع التأكد من أن لكل منها شارعًا ومدينة مرتبطين به.
يجب أن يبدو مخطط جدول الأشخاص المحدّث تقريبًا هكذا:
\d people
Table "public.people"
Column | Type | Modifiers
-----------+-----------------------+--------------------------------------------
id | integer | not null
| | default nextval('people_id_seq'::regclass)
name | character varying(50) |
house_no | integer | not null
street_id | integer | not null
phone_no | character varying |
geom | geometry |
city_id | integer | not null
Indexes:
"people_pkey" PRIMARY KEY, btree (id)
"people_name_idx" btree (name)
Check constraints:
"people_geom_point_chk" CHECK (st_geometrytype(geom) =
'ST_Point'::text OR geom IS NULL)
Foreign-key constraints:
"people_city_id_fkey" FOREIGN KEY (city_id) REFERENCES cities(id)
"people_street_id_fkey" FOREIGN KEY (street_id) REFERENCES streets(id)
الإجابة
delete from people;
alter table people add column city_id int not null references cities(id);
(التقط المدن في QGIS)
insert into people (name,house_no, street_id, phone_no, city_id, geom)
values ('Faulty Towers',
34,
3,
'072 812 31 28',
1,
'SRID=4326;POINT(13 -15)');
insert into people (name,house_no, street_id, phone_no, city_id, geom)
values ('IP Knightly',
32,
1,
'071 812 31 28',
1,
'SRID=4326;POINT(18 -24)');
insert into people (name,house_no, street_id, phone_no, city_id, geom)
values ('Rusty Bedsprings',
39,
1,
'071 822 31 28',
1,
'SRID=4326;POINT(22 -25)');
إذا ظهرت لك رسالة الخطأ التالية:
ERROR: insert or update on table "people" violates foreign key constraint
"people_city_id_fkey"
DETAIL: Key (city_id)=(1) is not present in table "cities".
فهذا يعني أنك أثناء تجربة إنشاء المضلعات لجدول المدن قد حذفت بعضها وبدأت من جديد. ما عليك إلا التحقق من الإدخالات في جدول المدن واستخدام أي id موجود.
16.5.5. نظرة على المخطط¶
يجب أن يبدو المخطط الآن هكذا:
16.5.6. جرّب بنفسك: ★★★¶
أنشئ حدود المدن بحساب أصغر غلاف محدب (convex hull) لجميع العناوين في كل مدينة وحساب نطاق عازل حول تلك المنطقة.
الإجابة
أضف بعض الأشخاص في مدينة 'Tokyo Outer Wards'
INSERT INTO people (name, house_no, street_id, phone_no, city_id, geom) VALUES ('Bad Aboum', 57, 2, '073 712 31 21', 2, 'SRID=4326;POINT(22 18)'); INSERT INTO people (name, house_no, street_id, phone_no, city_id, geom) VALUES ('Pat Atra', 59, 2, '074 712 31 25', 2, 'SRID=4326;POINT(23 14)'); INSERT INTO people (name, house_no, street_id, phone_no, city_id, geom) VALUES ('Kat Herin', 65, 2, '074 722 31 28', 2, 'SRID=4326;POINT(29 18)');
أنشئ الجدول myPolygonTable
CREATE TABLE myPolygonTable ( id serial NOT NULL PRIMARY KEY, city_id int NOT NULL REFERENCES cities(id), geom geometry NOT NULL ); ALTER TABLE myPolygonTable ADD CONSTRAINT myPolygonTable_geom_polygon_chk CHECK (st_geometrytype(geom) = 'ST_Polygon'::text );
أنشئ الأغلفة المحدبة وحمّلها
INSERT INTO myPolygonTable (city_id, geom) SELECT * FROM ( SELECT ROW_NUMBER() over (order by city_id)::integer AS city_id, ST_CONVEXHULL(ST_COLLECT(geom)) AS geom FROM people GROUP BY city_id ) convexHulls;
16.5.7. الوصول إلى الكائنات الفرعية¶
مع دوال نموذج SFS، لديك مجموعة واسعة من الخيارات للوصول إلى الكائنات الفرعية لأشكال SFS الهندسية. عندما تريد تحديد نقطة الرأس الأول لكل شكل هندسي مضلع في الجدول myPolygonTable، عليك فعل ذلك بهذه الطريقة:
حوّل حدود المضلع إلى خط:
select st_boundary(geom) from myPolygonTable;
حدد نقطة الرأس الأول للخط الناتج:
select st_startpoint(myGeometry) from ( select st_boundary(geom) as myGeometry from myPolygonTable) as foo;
16.5.8. معالجة البيانات¶
يدعم PostGIS جميع الدوال المتوافقة مع معيار OGC SFS/MM، وتبدأ جميع هذه الدوال بـ ST_.
16.5.9. القص¶
لقص جزء فرعي من بياناتك يمكنك استخدام الدالة ST_INTERSECT(). ولتجنب الأشكال الهندسية الفارغة، استخدم:
where not st_isempty(st_intersection(a.geom, b.geom))
select st_intersection(a.geom, b.geom), b.*
from clip as a, road_lines as b
where not st_isempty(st_intersection(st_setsrid(a.geom,32734),
b.geom));
16.5.10. بناء أشكال هندسية من أشكال هندسية أخرى¶
تريد إنشاء خط من جدول نقاط معين. يُحدَّد ترتيب النقاط بواسطة id الخاص بها. ويمكن أن تكون طريقة الترتيب الأخرى طابعًا زمنيًا، مثل الذي تحصل عليه عند التقاط نقاط المسار بجهاز استقبال GPS.
لإنشاء خط من طبقة نقاط جديدة اسمها 'points'، يمكنك تشغيل الأمر التالي:
select ST_LineFromMultiPoint(st_collect(geom)), 1 as id
from (
select geom
from points
order by id
) as foo;
لترى كيف يعمل دون إنشاء طبقة جديدة، يمكنك أيضًا تشغيل هذا الأمر على الطبقة 'people'، وإن كان ذلك بالطبع لا معنى عمليًا له في الواقع.
16.5.11. تنظيف الأشكال الهندسية¶
يمكنك الحصول على مزيد من المعلومات حول هذا الموضوع في هذه التدوينة.
16.5.12. الفروق بين الجداول¶
لاكتشاف الفرق بين جدولين لهما البنية نفسها، يمكنك استخدام الكلمة المفتاحية EXCEPT في PostgreSQL:
select * from table_a
except
select * from table_b;
ستحصل نتيجةً لذلك على جميع السجلات من table_a غير المخزنة في table_b.
16.5.13. مساحات الجداول (Tablespaces)¶
يمكنك تحديد المكان الذي يخزن فيه postgres بياناته على القرص بإنشاء مساحات الجداول (tablespaces):
CREATE TABLESPACE homespace LOCATION '/home/pg';
عند إنشاء قاعدة بيانات، يمكنك بعد ذلك تحديد مساحة الجداول التي ستُستخدم، مثلًا:
createdb --tablespace=homespace t4a
16.5.14. الخلاصة¶
تعلمت كيف تنشئ أشكالًا هندسية أكثر تعقيدًا باستخدام عبارات PostGIS. تذكّر أن الغرض من ذلك في الغالب هو تحسين معرفتك الضمنية عند العمل مع قواعد البيانات المدعمة جغرافيًا عبر واجهة نظم المعلومات الجغرافية (GIS). فلن تحتاج عادةً إلى إدخال هذه العبارات يدويًا، لكن امتلاك فكرة عامة عن بنيتها سيساعدك عند استخدام نظم المعلومات الجغرافية، خاصة إذا واجهت أخطاء قد تبدو غامضة لولا ذلك.