Skip to content

Commit

Permalink
Merge pull request #45 from pyturf/square_grid
Browse files Browse the repository at this point in the history
Add grid modules block
  • Loading branch information
diogomatoschaves committed Nov 22, 2020
2 parents 5adc781 + d80297b commit 7eedb83
Show file tree
Hide file tree
Showing 83 changed files with 167,604 additions and 1 deletion.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,18 +87,23 @@ Currently, the following modules have been implemented:
- [explode](https://github.com/pyturf/pyturf/tree/master/turf/explode)
- [great circle](https://github.com/pyturf/pyturf/tree/master/turf/great_circle)
- [helpers](https://github.com/pyturf/pyturf/tree/master/turf/helpers)
- [hex_grid](https://github.com/pyturf/pyturf/tree/master/turf/hex_grid)
- [length](https://github.com/pyturf/pyturf/tree/master/turf/length)
- [line-intersect](https://github.com/pyturf/pyturf/tree/master/turf/line_intersect)
- [midpoint](https://github.com/pyturf/pyturf/tree/master/turf/midpoint)
- [nearest-point](https://github.com/pyturf/pyturf/tree/master/turf/nearest_point)
- [point-grid](https://github.com/pyturf/pyturf/tree/master/turf/point_grid)
- [point-on-feature](https://github.com/pyturf/pyturf/tree/master/turf/point_on_feature)
- [point-to-line-distance](https://github.com/pyturf/pyturf/tree/master/turf/point_to_line_distance)
- [polygon-tangents](https://github.com/pyturf/pyturf/tree/master/turf/polygon_tangents)
- [polygon-to-line](https://github.com/pyturf/pyturf/tree/master/turf/polygon_to_line)
- [rectangle-grid](https://github.com/pyturf/pyturf/tree/master/turf/rectangle_grid)
- [rhumb-bearing](https://github.com/pyturf/pyturf/tree/master/turf/rhumb_bearing)
- [rhumb-destination](https://github.com/pyturf/pyturf/tree/master/turf/rhumb_destination)
- [rhumb-distance](https://github.com/pyturf/pyturf/tree/master/turf/rhumb_distance)
- [square](https://github.com/pyturf/pyturf/tree/master/turf/square)
- [square-grid](https://github.com/pyturf/pyturf/tree/master/turf/square_grid)
- [triangle-grid](https://github.com/pyturf/pyturf/tree/master/turf/triangle_grid)

## Contributing

Expand Down
29 changes: 29 additions & 0 deletions docs/source/modules/grids.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,32 @@
Grids
=====

Hex Grid
--------

.. autofunction:: turf.hex_grid


Point Grid
----------

.. autofunction:: turf.point_grid


Rectangle Grid
--------------

.. autofunction:: turf.rectangle_grid


Square Grid
-----------

.. autofunction:: turf.square_grid


Triangle Grid
-------------

.. autofunction:: turf.triangle_grid
5 changes: 5 additions & 0 deletions turf/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,21 @@
from turf.explode import explode
from turf.great_circle import great_circle
from turf.helpers import *
from turf.hex_grid import hex_grid
from turf.length import length
from turf.line_intersect import line_intersect
from turf.midpoint import midpoint
from turf.nearest_point import nearest_point
from turf.point_grid import point_grid
from turf.point_on_feature import point_on_feature
from turf.point_to_line_distance import point_to_line_distance
from turf.polygon_tangents import polygon_tangents
from turf.polygon_to_line import polygon_to_line
from turf.rectangle_grid import rectangle_grid
from turf.rhumb_bearing import rhumb_bearing
from turf.rhumb_destination import rhumb_destination
from turf.rhumb_distance import rhumb_distance
from turf.square import square
from turf.square_grid import square_grid
from turf.triangle_grid import triangle_grid
from turf.version import __version__
2 changes: 1 addition & 1 deletion turf/distance/_distance.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def distance(start, end, options=None):

kwargs = {}
if isinstance(options, dict) and "units" in options:
kwargs.update(options)
kwargs.update({"units": options.get("units")})

coordinates1 = get_coords_from_features(start, ["Point"])
coordinates2 = get_coords_from_features(end, ["Point"])
Expand Down
1 change: 1 addition & 0 deletions turf/hex_grid/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from turf.hex_grid._hex_grid import hex_grid
170 changes: 170 additions & 0 deletions turf/hex_grid/_hex_grid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
from typing import Dict, List, Union
import math

from turf.distance import distance
from turf.bbox import bbox
from turf.boolean_intersects import boolean_intersects
from turf.helpers import feature_collection, polygon, FeatureCollection


def hex_grid(
bbox: List[float], cell_side: Union[int, float], options: Dict = {},
) -> FeatureCollection:
"""
Takes a bounding box and the diameter of the cell and returns a FeatureCollection of flat-topped
hexagons or triangles aligned in an "odd-q" vertical grid as
described in [Hexagonal Grids](http://www.redblobgames.com/grids/hexagons/).
:param bbox: Array extent in [minX, minY, maxX, maxY] order
:param n_cells: length of the side of the the hexagons or triangles, in units. It will also coincide with the
radius of the circumcircle of the hexagons
:param options: Optional parameters
[options["units"]]: units ("degrees", "radians", "miles", "kilometers")
of the given cell_width and cell_height
[options["mask"]]: if passed a Polygon or MultiPolygon here,
the grid Points will be created only inside it
[options["properties"]]: passed to each point of the grid
[options["triangles"]]: whether to return as triangles instead of hexagons
:returns: FeatureCollection of a grid of polygons
"""
if not isinstance(options, dict):
options = {}

has_triangles = options.get("triangles", None)

results = []
west = bbox[0]
south = bbox[1]
east = bbox[2]
north = bbox[3]

center_y = (south + north) / 2
center_x = (west + east) / 2

x_fraction = (cell_side * 2) / (
distance([west, center_y], [east, center_y], options)
)
cell_width_deg = x_fraction * (east - west)
y_fraction = (
cell_side * 2 / (distance([center_x, south], [center_x, north], options))
)
cell_height_deg = y_fraction * (north - south)
radius = cell_width_deg / 2

hex_width = radius * 2
hex_height = math.sqrt(3) / 2 * cell_height_deg

# rows & columns
bbox_width = east - west
bbox_height = north - south

x_interval = 3 / 4 * hex_width
y_interval = hex_height

x_span = (bbox_width - hex_width) / (hex_width - radius / 2)
x_count = int(x_span)

x_adjust = (
((x_count * x_interval - radius / 2) - bbox_width) / 2
- radius / 2
+ x_interval / 2
)

y_count = int((bbox_height - hex_height) / hex_height)
y_adjust = (bbox_height - y_count * hex_height) / 2

has_offset_y = (y_count * hex_height - bbox_height) > (hex_height / 2)

if has_offset_y:
y_adjust -= hex_height / 4

cosines = []
sines = []

for i in range(6):
angle = 2 * math.pi / 6 * i
cosines.append(math.cos(angle))
sines.append(math.sin(angle))

results = []

for x in range(x_count + 1):
for y in range(y_count + 1):

is_odd = x % 2 == 1

if (y == 0) and is_odd:
continue

if (y == 0) and has_offset_y:
continue

center_x = x * x_interval + west - x_adjust
center_y = y * y_interval + south + y_adjust

if is_odd:
center_y -= hex_height / 2

if has_triangles:

triangles = hex_triangles(
[center_x, center_y],
cell_width_deg / 2,
cell_height_deg / 2,
options.get("properties", {}).copy(),
cosines,
sines,
)

for triangle in triangles:
if "mask" in options:
if boolean_intersects(options["mask"], triangle):
results.append(triangle)
else:
results.append(triangle)

else:

hex = hexagon(
[center_x, center_y],
cell_width_deg / 2,
cell_height_deg / 2,
options.get("properties", {}).copy(),
cosines,
sines,
)

if "mask" in options:
if boolean_intersects(options["mask"], hex):
results.append(hex)
else:
results.append(hex)

return feature_collection(results)


def hexagon(center, rx, ry, properties, cosines, sines):
vertices = []
for i in range(6):
x = center[0] + rx * cosines[i]
y = center[1] + ry * sines[i]
vertices.append([x, y])

vertices.append(vertices[0])
return polygon([vertices], properties)


def hex_triangles(center, rx, ry, properties, cosines, sines):
triangles = []
for i in range(6):
vertices = []
vertices.append(center)
vertices.append([center[0] + rx * cosines[i], center[1] + ry * sines[i]])
vertices.append(
[center[0] + rx * cosines[(i + 1) % 6], center[1] + ry * sines[(i + 1) % 6]]
)
vertices.append(center)
triangles.append(polygon([vertices], properties))

return triangles
11 changes: 11 additions & 0 deletions turf/hex_grid/tests/in/bbox1-triangles.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"bbox": [
-96.6357421875,
31.12819929911196,
-84.9462890625,
40.58058466412764
],
"cellSide": 25,
"units": "miles",
"triangles": true
}
10 changes: 10 additions & 0 deletions turf/hex_grid/tests/in/bbox1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"bbox": [
-96.6357421875,
31.12819929911196,
-84.9462890625,
40.58058466412764
],
"cellSide": 25,
"units": "miles"
}
10 changes: 10 additions & 0 deletions turf/hex_grid/tests/in/big-bbox.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"bbox": [
-220.78125,
-80.64703474739618,
-29.53125,
78.34941069014629
],
"cellSide": 500,
"units": "miles"
}
10 changes: 10 additions & 0 deletions turf/hex_grid/tests/in/fiji-10-miles.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"bbox": [
-180.3460693359375,
-17.16703442146408,
-179.5770263671875,
-16.48349760264812
],
"cellSide": 10,
"units": "miles"
}
10 changes: 10 additions & 0 deletions turf/hex_grid/tests/in/london-20-miles.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"bbox": [
-0.6207275390625,
51.23784668914442,
0.439453125,
51.767839887322154
],
"cellSide": 20,
"units": "miles"
}

0 comments on commit 7eedb83

Please sign in to comment.