-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathutilities.py
More file actions
130 lines (117 loc) · 4.86 KB
/
utilities.py
File metadata and controls
130 lines (117 loc) · 4.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
from qgis.core import QgsFeature, QgsPointXY, QgsGeometry
import numpy as np
from scipy.spatial.distance import pdist
from scipy.spatial.distance import squareform
from scipy.optimize import minimize
def getPointCoords(feat, weightFieldIndex):
"""Extract x, y coordinate lists and associated weights from features.
Parameters
----------
feat : iterable
Iterable of QgsFeature like objects.
weightFieldIndex : int
Index of the field containing the weight value. A negative value
treats all weights as 1.
Returns
-------
tuple
Three element tuple of (x coordinates, y coordinates, weights).
Raises
------
ValueError
If any feature is missing required geometry/attributes or if the
iterator is empty.
"""
try:
pts = []
weights = []
for f in feat:
pts.append(f.geometry().asPoint())
if weightFieldIndex >= 0:
weights.append(f.attributes()[weightFieldIndex])
else:
weights.append(1)
if not pts:
raise ValueError("No features provided")
weights = np.asarray(weights, dtype=np.float32)
x = [pt[0] for pt in pts]
y = [pt[1] for pt in pts]
return x, y, weights
except (AttributeError, IndexError, TypeError, ValueError) as exc:
raise ValueError("Invalid feature data") from exc
def getMeanCenter(x, y, weights, id):
"""Return a feature representing the weighted mean center."""
try:
x = np.asarray(x, dtype=np.float32)
y = np.asarray(y, dtype=np.float32)
weights = np.asarray(weights, dtype=np.float32)
if weights.size == 0 or np.sum(weights) == 0:
raise ValueError("Weights must not be empty or all zeros")
wx = x * weights
wy = y * weights
mx = np.sum(wx) / np.sum(weights)
my = np.sum(wy) / np.sum(weights)
meanCenter = QgsPointXY(mx, my)
centerFeat = QgsFeature()
centerGeom = QgsGeometry.fromPointXY(meanCenter)
attrs = centerFeat.attributes()
centerFeat.setGeometry(centerGeom)
attrs.extend([id])
centerFeat.setAttributes(attrs)
return centerFeat
except (ZeroDivisionError, TypeError, ValueError) as exc:
raise ValueError("Failed to compute mean center") from exc
def getMedianCenter(x, y, weights, id):
"""Return a feature representing the weighted median center."""
try:
x = np.asarray(x, dtype=np.float32)
y = np.asarray(y, dtype=np.float32)
weights = np.asarray(weights, dtype=np.float32)
sumWeights = np.sum(weights)
if weights.size == 0 or sumWeights == 0:
raise ValueError("Weights must not be empty or all zeros")
mx = np.sum(x) / len(x)
my = np.sum(y) / len(y)
# initial guesses
cMedianCenter = np.zeros(2)
cMedianCenter[0] = mx
cMedianCenter[1] = my
# define objective
def objective(cMedianCenter):
return np.sum(np.sqrt((cMedianCenter[0] - x) ** 2 +
(cMedianCenter[1] - y) ** 2) * weights / sumWeights)
solution = minimize(objective, cMedianCenter, method='Nelder-Mead')
medianCenter = QgsPointXY(solution.x[0], solution.x[1])
centerFeat = QgsFeature()
centerGeom = QgsGeometry.fromPointXY(medianCenter)
attrs = centerFeat.attributes()
centerFeat.setGeometry(centerGeom)
attrs.extend([id])
centerFeat.setAttributes(attrs)
return centerFeat
except (ZeroDivisionError, TypeError, ValueError) as exc:
raise ValueError("Failed to compute median center") from exc
def getCentralFeature(x, y, weights, id, dMetricIndex):
"""Return a feature representing the weighted central feature."""
try:
dMetric = ['euclidean', 'cityblock']
x = np.asarray(x, dtype=np.float32)
y = np.asarray(y, dtype=np.float32)
weights = np.asarray(weights, dtype=np.float32)
sumWeights = np.sum(weights)
if weights.size == 0 or sumWeights == 0:
raise ValueError("Weights must not be empty or all zeros")
coords = np.stack([x, y], axis=-1)
distanceVector = pdist(coords, metric=dMetric[dMetricIndex])
distanceMatrix = squareform(distanceVector) / weights * sumWeights
minDistanceIndex = np.argmin(np.sum(distanceMatrix, axis=1))
centralFeat = QgsFeature()
centralGeom = QgsGeometry.fromPointXY(QgsPointXY(x[minDistanceIndex], y[minDistanceIndex]))
attrs = centralFeat.attributes()
centralFeat.setGeometry(centralGeom)
attrs.extend([id])
centerFeat = centralFeat
centerFeat.setAttributes(attrs)
return centerFeat
except (IndexError, ZeroDivisionError, TypeError, ValueError) as exc:
raise ValueError("Failed to compute central feature") from exc