-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgrid_manager.py
160 lines (135 loc) · 5.96 KB
/
grid_manager.py
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
from __future__ import division
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from scipy import sparse
from bisect import bisect_left
class Grid:
'''
Manage landscapes as grids, represented by a sparse matrix of affinities.
Allows to do plots and to do conversions between the different coordinates of nodes (id in the graph, physical coordinates, grid coordinates)
columns of the grid are parallels to the y axis of the landscape, and rows to the x axis.
rows and columns are numbered from the top left corner.
The order of nodes in the graph correspond to reading the grid from left to right, top to bottom.
i.e. if there are n columns, the pixel on the ith rows, jth column correspond to the node i*n+j
All numbering start with 0.
Physical coordinates take the form (value on the y axis, value on the x axis)
Grid coordinates take the form (row id, column id)
Instance variables:
- A : affinity matrix in the csr format. Great for matrix-vector multiplications
- A_dok : affinity matrix in the dok format. Access to edge weight in O(1)
- map_size : tuple (size along the y axis, size along the x axis). This is the physical size (in meters or kilometers) of the landscape.
- shape : tuple (number of rows, number of columns). This is the size of the grid representing the landscape
- pixel_size : tuple (pixel size along the y axis, pixel size along the x axis)
- x_ticks : list delimitating the pixels along the x axis (in the physical coordinates). The ith column is delimitated by x_ticks[i] and x_ticks[i+1].
if there are n colums, len(x_ticks) will be n+1.
- y_ticks : list delimitating the pixels along the y axis (in the physical coordinates). The ith row is delimitated by y_ticks[i] and y_ticks[i+1].
if there are n rows, len(y_ticks) will be n+1.
'''
def __init__(self, map_size, shape, graph=None, qualities=None):
'''
Inputs:
- graph : sparse matrix (if None, the graph can be set later using set_affinities)
- map_size : tuple (size along the y axis, size along the x axis). This is the physical size (in meters or kilometers) of the landscape.
- shape : tuple (number of rows, number of columns). This is the size of the grid representing the landscape
'''
self.pixel_size = (map_size[0]/shape[0], map_size[1]/shape[1])
self.shape = shape
self.map_size = map_size
self.n_rows, self.n_cols = shape
self.N = shape[0] * shape[1]
self.x_ticks = np.linspace(0, map_size[1], num=(shape[1]+1))
self.y_ticks = np.linspace(0, map_size[0], num=(shape[0]+1))
if graph is not None:
self.set_affinities(graph)
if qualities is not None:
self.qualities = qualities
else:
self.qualities = np.ones(self.N)
def set_affinities(self, graph):
'''
Set the affinity matrix A and A_dok.
graph should be a sparse matrix.
'''
if graph.shape[0] != self.N:
raise ValueError('The shape of the grid does not match the number of nodes of the graph')
self.A = graph.tocsr()
self.A_dok = graph.todok()
def node_id_to_grid_coordinates(self, node_id):
'''
Return the grid coordinates (row id, column id) corresponding to a given node
'''
self.check_node_id(node_id)
j = int(node_id % self.n_cols)
i = (node_id - j) / self.n_cols
return (i, j)
def node_id_to_coordinates(self, node_id):
'''
Return the physical coordinates of a node (center of the corresponding pixel)
'''
return self.grid_coordinates_to_coordinates(self.node_id_to_grid_coordinates(node_id))
def coordinates_to_grid_coordinates(self, coo):
'''
Return the grid coordinates (row id, column id) of the pixel containing the
physical coordinates "coo" (value along the y axis, value along the x axis)
'''
self.check_coordinates(coo)
i = max(0, bisect_left(self.y_ticks, coo[0]) - 1)
j = max(0, bisect_left(self.x_ticks, coo[1]) - 1)
return (i,j)
def coordinates_to_node_id(self, coo):
'''
Return the id of the node containing the physical coordinates "coo"
the coordinates are interpreted as (value along the y axis, value along the x axis)
'''
return self.grid_coordinates_to_node_id(self.coordinates_to_grid_coordinates(coo))
def grid_coordinates_to_node_id(self, grid_coo):
'''
Return the id of the node corresponding to the grid coordinates "grid_coo" (row id, column id)
'''
self.check_grid_coordinates(grid_coo)
return grid_coo[0] * self.n_cols + grid_coo[1]
def grid_coordinates_to_coordinates(self, grid_coo):
'''
Return the physical coordinates of the center of the pixel identified by "grid_coo" (row id, column id)
'''
self.check_grid_coordinates(grid_coo)
return ((self.y_ticks[grid_coo[0]] + self.y_ticks[grid_coo[0] + 1])/2, (self.x_ticks[grid_coo[1]] + self.x_ticks[grid_coo[1] + 1])/2)
def check_grid_coordinates(self, grid_coo):
'''
Check if grid coordinates are valid. Raises IndexError if not
'''
if grid_coo[0] >= self.n_rows or grid_coo[0] < 0:
raise IndexError('row id outside the range')
if grid_coo[1] >= self.n_cols or grid_coo[1] < 0:
raise IndexError('column id outside the range')
def check_coordinates(self, coo):
'''
Check if physical coordinates are valid. Raises IndexError if not
'''
if coo[0] > self.map_size[0] or coo[0] < 0:
raise IndexError('y coordinate outside the range')
if coo[1] > self.map_size[1] or coo[1] < 0:
raise IndexError('x coordinate outside the range')
def check_node_id(self, node_id):
'''
Check if node id is valid. Raises IndexError if not
'''
if node_id >= self.N or node_id < 0:
raise IndexError('Node id outside the range')
def plot_indegrees(self):
indegrees = self.A.transpose().dot(np.ones((self.N, )))
self.plot(indegrees)
def plot_outdegrees(self):
outdegrees = self.A.dot(np.ones((self.N, )))
self.plot(outdegrees)
def plot(self, values, source=None):
if len(values) != self.N:
raise ValueError('Needs a value for each pixel')
z = np.reshape(values, self.shape)
plt.figure()
plt.imshow(z, cmap='RdYlGn', interpolation='nearest')
plt.colorbar()
if source is not None:
plt.sc
plt.show()