Assign alternating values to a regular grid of points in Geopandas

Viewed 27

I have a point grid generated from a bounding box of a polygon using numpy's meshgrid.

The code is below, but I want to make sure each point has an A or B.

The points in the first row should be labeled A B A B A B A B etc...

The points in the second row should be labeled B A B A B A B A etc...

This should alternate, so all the odd rows should follow the pattern of the first row and all the even rows should follow the pattern in the second row.

minx = min(np.floor(polygon.bounds.minx / grid_res) * grid_res)
maxx = max(np.ceil(polygon.bounds.maxx / grid_res) * grid_res)
miny = min(np.floor(polygon.bounds.miny / grid_res) * grid_res)
maxy = max(np.ceil(polygon.bounds.maxy / grid_res) * grid_res)

# Make a grid
x = np.arange(minx, maxx + grid_res / 2, grid_res)
y = np.arange(miny, maxy + grid_res / 2, grid_res)
x, y = np.meshgrid(x, y)

points = MultiPoint(gpd.points_from_xy(np.ravel(x), np.ravel(y)))
points = gpd.GeoDataFrame(crs=crs, geometry=[points])
points = points.explode(index_parts=True)

It would be handy if the np.meshgrid could assign the A or B, alternatively in Geopandas once the MultiPoint is exploded the point grid could be iterated over row by row.

Which is the best way, and how do I do it?

EDIT

To help:

x = array([388900., 389000., 389100., 389200., 389300., 389400., 389500., 389600., 389700.])

y = array([107800., 107900., 108000., 108100., 108200., 108300., 108400.])
1 Answers

I don't exactly follow where you want to assign A and B or what A and B are anyway, but you can use numpy to create an alternating array of 0 and 1 (and transform it from there) quite easily:

...
x, y = np.meshgrid(x, y)
labels = sum(np.meshgrid(range(x.shape[0]), range(x.shape[1]))) % 2

#array([[0, 1, 0, 1], #example for 4x4 array
#       [1, 0, 1, 0],
#       [0, 1, 0, 1],
#       [1, 0, 1, 0]])

There's probably an even easier and more efficient way to do it, but this will get the job done.

Basically we grab the dimensions of your array of points from the result of the first meshgrid (because you overwrite x and y from which we could have grabbed len(x) or something). Then creating a new meshgrid with simple range calls gets us an array like the following:

>>>np.meshgrid(range(4), range(4))
[array([[0, 1, 2, 3],
        [0, 1, 2, 3],
        [0, 1, 2, 3],
        [0, 1, 2, 3]]),
 array([[0, 0, 0, 0],
        [1, 1, 1, 1],
        [2, 2, 2, 2],
        [3, 3, 3, 3]])]

And taking the sum of the resulting list of arrays:

array([[0, 1, 2, 3],
       [1, 2, 3, 4],
       [2, 3, 4, 5],
       [3, 4, 5, 6]])

due to the way we made it, this array will be the same dimensions as x and y, and will have diagonals following range(shape[0]+shape[1]-1). Then it's as simple as testing if each element is even or odd with %2. This technique could be adapted to more than just A or B by tweaking the range calls as well as adjusting the % x:

>>> sum(np.meshgrid(range(5), range(0,10,2))) % 4
array([[0, 1, 2, 3, 0],  # a fun pattern where every 2x2 contains 0-3
       [2, 3, 0, 1, 2],
       [0, 1, 2, 3, 0],
       [2, 3, 0, 1, 2],
       [0, 1, 2, 3, 0]], dtype=int32)

EDIT: It was bugging me that I knew there was an easier way without sum(np.meshgrid(, and I remembered that you can simply broadcast addition with np.newaxis:

>>> (np.arange(4)[:, None] + np.arange(4)) % 2
array([[0, 1, 0, 1],
       [1, 0, 1, 0],
       [0, 1, 0, 1],
       [1, 0, 1, 0]], dtype=int32)
Related