7. التعامل مع الأشكال الهندسية

تلميح

تحتاج مقتطفات الشيفرة في هذه الصفحة إلى الاستيرادات التالية إذا كنت خارج وحدة تحكم pyqgis:

 1from qgis.core import (
 2  QgsGeometry,
 3  QgsGeometryCollection,
 4  QgsPoint,
 5  QgsPointXY,
 6  QgsWkbTypes,
 7  QgsProject,
 8  QgsFeatureRequest,
 9  QgsVectorLayer,
10  QgsDistanceArea,
11  QgsUnitTypes,
12  QgsCoordinateTransform,
13  QgsCoordinateReferenceSystem
14)

يُشار عادةً إلى النقاط والخطوط والمضلعات التي تمثّل معلمًا مكانيًا باسم الأشكال الهندسية. وتُمثَّل في QGIS بالصنف QgsGeometry.

أحيانًا يكون الشكل الهندسي الواحد في الواقع مجموعة من الأشكال الهندسية البسيطة (أحادية الجزء). ويُسمى مثل هذا الشكل شكلًا هندسيًا متعدد الأجزاء. وإذا احتوى على نوع واحد فقط من الأشكال البسيطة، نسمّيه نقاطًا متعددة أو خطوطًا متعددة أو مضلعات متعددة. على سبيل المثال، يمكن تمثيل دولة مكوّنة من جزر متعددة على هيئة مضلعات متعددة.

يمكن أن تكون إحداثيات الأشكال الهندسية بأي نظام إحداثيات مرجعي (CRS). وعند جلب المعالم من طبقة، ستكون إحداثيات الأشكال الهندسية المرتبطة بها بنظام الإحداثيات المرجعي للطبقة.

يتوفر وصف ومواصفات جميع طرق بناء الأشكال الهندسية الممكنة والعلاقات بينها في معايير OGC Simple Feature Access للاطلاع على التفاصيل المتقدمة.

7.1. بناء الأشكال الهندسية

يوفّر PyQGIS عدة خيارات لإنشاء شكل هندسي:

  • من الإحداثيات

    1gPnt = QgsGeometry.fromPointXY(QgsPointXY(1,1))
    2print(gPnt)
    3gLine = QgsGeometry.fromPolyline([QgsPoint(1, 1), QgsPoint(2, 2)])
    4print(gLine)
    5gPolygon = QgsGeometry.fromPolygonXY([[QgsPointXY(1, 1),
    6  QgsPointXY(2, 2), QgsPointXY(2, 1)]])
    7print(gPolygon)
    

    تُعطى الإحداثيات باستخدام الصنف QgsPoint أو الصنف QgsPointXY. والفرق بين هذين الصنفين أن QgsPoint يدعم البعدين M و Z.

    يُمثَّل الخط المتعدد (Linestring) بقائمة من النقاط.

    يُمثَّل المضلع بقائمة من الحلقات الخطية (أي خطوط مغلقة). الحلقة الأولى هي الحلقة الخارجية (الحد)، والحلقات اللاحقة الاختيارية هي ثقوب في المضلع. لاحظ أن QGIS، على خلاف بعض البرامج، سيغلق الحلقة نيابةً عنك، فلا حاجة لتكرار النقطة الأولى في آخر الحلقة.

    تذهب الأشكال الهندسية متعددة الأجزاء مستوى أبعد: فالنقاط المتعددة قائمة نقاط، والخطوط المتعددة قائمة خطوط، والمضلعات المتعددة قائمة مضلعات.

  • من النص المعروف (WKT)

    geom = QgsGeometry.fromWkt("POINT(3 4)")
    print(geom)
    
  • من الصيغة الثنائية المعروفة (WKB)

    1g = QgsGeometry()
    2wkb = bytes.fromhex("010100000000000000000045400000000000001440")
    3g.fromWkb(wkb)
    4
    5# print WKT representation of the geometry
    6print(g.asWkt())
    

7.2. الوصول إلى الشكل الهندسي

أولًا، عليك معرفة نوع الشكل الهندسي. والدالة التي تُستخدم لذلك هي wkbType()، وهي تُعيد قيمة من التعداد QgsWkbTypes.Type.

1print(gPnt.wkbType())
2# output: WkbType.Point
3print(gLine.wkbType())
4# output: WkbType.LineString
5print(gPolygon.wkbType())
6# output: WkbType.Polygon

وكبديل، يمكن استخدام الدالة type() التي تُعيد قيمة من التعداد QgsWkbTypes.GeometryType.

print(gLine.type())
# output: GeometryType.Line

يمكنك استخدام الدالة displayString() للحصول على نوع الشكل الهندسي بصيغة مقروءة للإنسان.

1print(QgsWkbTypes.displayString(gPnt.wkbType()))
2# output: 'Point'
3print(QgsWkbTypes.displayString(gLine.wkbType()))
4# output: 'LineString'
5print(QgsWkbTypes.displayString(gPolygon.wkbType()))
6# output: 'Polygon'

توجد أيضًا دالة مساعدة isMultipart() لمعرفة ما إذا كان الشكل الهندسي متعدد الأجزاء أم لا.

لاستخراج المعلومات من شكل هندسي توجد دوال وصول لكل نوع متجهي. إليك مثالًا على كيفية استخدام دوال الوصول هذه:

1print(gPnt.asPoint())
2# output: <QgsPointXY: POINT(1 1)>
3print(gLine.asPolyline())
4# output: [<QgsPointXY: POINT(1 1)>, <QgsPointXY: POINT(2 2)>]
5print(gPolygon.asPolygon())
6# output: [[<QgsPointXY: POINT(1 1)>, <QgsPointXY: POINT(2 2)>, <QgsPointXY: POINT(2 1)>, <QgsPointXY: POINT(1 1)>]]

ملاحظة

الثنائيات (x,y) ليست ثنائيات (tuples) حقيقية، بل هي كائنات QgsPoint، ويمكن الوصول إلى قيمها بالدالتين x() و y().

وللأشكال الهندسية متعددة الأجزاء توجد دوال وصول مماثلة: asMultiPoint() و asMultiPolyline() و asMultiPolygon().

يمكن التكرار على جميع أجزاء الشكل الهندسي بغض النظر عن نوعه. مثلًا:

geom = QgsGeometry.fromWkt( 'MultiPoint( 0 0, 1 1, 2 2)' )
for part in geom.parts():
  print(part.asWkt())
Point (0 0)
Point (1 1)
Point (2 2)
geom = QgsGeometry.fromWkt( 'LineString( 0 0, 10 10 )' )
for part in geom.parts():
  print(part.asWkt())
LineString (0 0, 10 10)
gc = QgsGeometryCollection()
gc.fromWkt('GeometryCollection( Point(1 2), Point(11 12), LineString(33 34, 44 45))')
print(gc[1].asWkt())
Point (11 12)

ويمكن أيضًا تعديل كل جزء من الشكل الهندسي باستخدام الدالة QgsGeometry.parts().

1geom = QgsGeometry.fromWkt( 'MultiPoint( 0 0, 1 1, 2 2)' )
2for part in geom.parts():
3  part.transform(QgsCoordinateTransform(
4    QgsCoordinateReferenceSystem("EPSG:4326"),
5    QgsCoordinateReferenceSystem("EPSG:3111"),
6    QgsProject.instance())
7  )
8
9print(geom.asWkt())
MultiPoint ((-10334728.12541878595948219 -5360106.25905461423099041),(-10462135.16126426123082638 -5217485.4735023295506835),(-10589399.84444035589694977 -5072021.45942386891692877))

7.3. شروط الأشكال الهندسية والعمليات عليها

يستخدم QGIS مكتبة GEOS لعمليات الأشكال الهندسية المتقدمة مثل الشروط الهندسية (contains()، intersects()، …) وعمليات المجموعات (combine()، difference()، …). ويمكنه أيضًا حساب الخصائص الهندسية للأشكال، مثل المساحة (في حالة المضلعات) أو الأطوال (للمضلعات والخطوط).

لنرَ مثالًا يجمع بين التكرار على المعالم في طبقة معيّنة وإجراء بعض الحسابات الهندسية بناءً على أشكالها الهندسية. ستحسب الشيفرة أدناه وتطبع مساحة ومحيط كل دولة في الطبقة countries ضمن مشروع QGIS التعليمي الخاص بنا.

تفترض الشيفرة التالية أن layer كائن QgsVectorLayer نوع معالمه مضلع (Polygon).

 1# let's access the 'countries' layer
 2layer = QgsProject.instance().mapLayersByName('countries')[0]
 3
 4# let's filter for countries that begin with Z, then get their features
 5query = '"name" LIKE \'Z%\''
 6features = layer.getFeatures(QgsFeatureRequest().setFilterExpression(query))
 7
 8# now loop through the features, perform geometry computation and print the results
 9for f in features:
10  geom = f.geometry()
11  name = f.attribute('NAME')
12  print(name)
13  print('Area: ', geom.area())
14  print('Perimeter: ', geom.length())
1Zambia
2Area:  62.82279065343119
3Perimeter:  50.65232014052552
4Zimbabwe
5Area:  33.41113559136517
6Perimeter:  26.608288555013935

لقد حسبت الآن مساحات الأشكال الهندسية ومحيطاتها وطبعتها. لكنك قد تلاحظ سريعًا أن القيم غريبة. وذلك لأن المساحات والمحيطات لا تأخذ نظام الإحداثيات المرجعي في الحسبان عند حسابها باستخدام الدالتين area() و length() من الصنف QgsGeometry. ولحساب أدق للمساحات والمسافات، يمكن استخدام الصنف QgsDistanceArea الذي يستطيع إجراء حسابات مبنية على الإهليلج (ellipsoid):

تفترض الشيفرة التالية أن layer كائن QgsVectorLayer نوع معالمه مضلع (Polygon).

 1d = QgsDistanceArea()
 2d.setEllipsoid('WGS84')
 3
 4layer = QgsProject.instance().mapLayersByName('countries')[0]
 5
 6# let's filter for countries that begin with Z, then get their features
 7query = '"name" LIKE \'Z%\''
 8features = layer.getFeatures(QgsFeatureRequest().setFilterExpression(query))
 9
10for f in features:
11  geom = f.geometry()
12  name = f.attribute('NAME')
13  print(name)
14  print("Perimeter (m):", d.measurePerimeter(geom))
15  print("Area (m2):", d.measureArea(geom))
16
17  # let's calculate and print the area again, but this time in square kilometers
18  print("Area (km2):", d.convertAreaMeasurement(d.measureArea(geom), QgsUnitTypes.AreaSquareKilometers))
1Zambia
2Perimeter (m): 5539361.250294601
3Area (m2): 751989035032.9031
4Area (km2): 751989.0350329031
5Zimbabwe
6Perimeter (m): 2865021.3325076113
7Area (m2): 389267821381.6008
8Area (km2): 389267.8213816008

أو قد ترغب بدلًا من ذلك في معرفة المسافة بين نقطتين.

 1d = QgsDistanceArea()
 2d.setEllipsoid('WGS84')
 3
 4# Let's create two points.
 5# Santa claus is a workaholic and needs a summer break,
 6# lets see how far is Tenerife from his home
 7santa = QgsPointXY(25.847899, 66.543456)
 8tenerife = QgsPointXY(-16.5735, 28.0443)
 9
10print("Distance in meters: ", d.measureLine(santa, tenerife))

يمكنك العثور على أمثلة كثيرة لخوارزميات مضمّنة في QGIS تستخدم هذه الدوال لتحليل البيانات المتجهية وتحويلها. فيما يلي روابط إلى شيفرة بعضٍ منها.