from cytoolz import get_in
import functools
import logging
import numpy as np
logger = logging.getLogger(__name__)
[docs]def minbox(points):
"""Returns the minimal bounding box necessary to contain points
Args:
points (tuple, list, set): ((0,0), (40, 55), (66, 22))
Returns:
dict: {ulx, uly, lrx, lry}
Example:
>>> minbox((0, 0), (40, 55), (66,22))
{'ulx': 0, 'uly': 55, 'lrx': 66, 'lry': 0}
"""
x, y = [point[0] for point in points], [point[1] for point in points]
return {'ulx': min(x), 'lrx': max(x), 'lry': min(y), 'uly': max(y)}
[docs]@functools.singledispatch
def coordinates(points, grid, snap_fn):
"""Returns grid coordinates contained within points.
Points may be specified as dicts, tuples, lists, or sets.
- dict with keys ulx, uly, lrx, lry
- sequence of sequences: ((0,0), (100, 167), (-212, 6621))
Irregular perimeters may be specified in sequences as points will be minboxed.
Points as dicts are an implicit minbox.
Args:
points (collection): Points outlining an area
grid (dict) : The target grid: {'name': 'chip', 'sx': 3000, 'sy': 3000, 'rx': 1, 'ry': -1}
snap_fn (func) : A function that accepts x, y and returns a snapped x, y
Returns:
tuple: tuple of tuples of grid coordinates ((x1,y1), (x2,y2) ...)
Example:
>>> grid = {'name': 'chip', 'sx': 500, 'sy': 500, 'rx': 1, 'ry': 1}
>>> sfn = partial(chipmunk.snap, url='http://localhost:5656')
>>> coordinates({'ulx': -1001, 'uly': 1000, 'lrx': -500, 'lry': 500},
grid=grid,
snap_fn=sfn)
((-3585.0, 2805.0),
(-3085.0, 2805.0),
(-2585.0, 2805.0),
(-2085.0, 2805.0),
(-1585.0, 2805.0),
(-1085.0, 2805.0),
(-585.0, 2805.0))
>>> grid = {'name': 'chip', 'sx': 3000, 'sy': 3000, 'rx': 1, 'ry': -1}
>>> coordinates(((112, 443), (112, 500), (100, 443)),
grid=grid,
snap_fn=sfn})
((-585.0, 2805.0),)
"""
logger.warn("coordinates() not implemented for type:{}".format(type(points)))
return None
@coordinates.register(dict)
def _(points, grid, snap_fn):
"""Returns grid coordinates contained within a dict of points.
Args:
points (dict): {'ulx': 0, 'uly': 15.5, 'lrx': 55.3, 'lry':-1000.12}
grid (dict): The target grid: {'name': 'chip', 'sx': 3000, 'sy': 3000, 'rx': 1, 'ry': -1}
snap_fn (func): A function that accepts x, y and returns a snapped x, y
Returns:
tuple: tuple of tuples of grid coordinates ((x1,y1), (x2,y1) ...)
This example assumes a grid size of 500x500 pixels.
Example:
>>> coordinates = coordinates({ulx: -1001, uly: 1000, lrx: -500, lry: 500},
grid={'name': 'chip', 'sx': 500, 'sy': 500, 'rx': 1, 'ry': -1},
snap_fn=some_func)
((-1000, 500), (-500, 500), (-1000, -500), (-500, -500))
"""
# snap start/end x & y
start_x, start_y = get_in([grid.get('name'), 'proj-pt'], snap_fn(x=points.get('ulx'), y=points.get('uly')))
end_x, end_y = get_in([grid.get('name'), 'proj-pt'], snap_fn(x=points.get('lrx'), y=points.get('lry')))
# get x and y scale factors multiplied by reflection
x_interval = grid.get('sx') * grid.get('rx')
y_interval = grid.get('sy') * grid.get('ry')
return tuple((x, y) for x in np.arange(start_x, end_x + x_interval, x_interval)
for y in np.arange(start_y, end_y + y_interval, y_interval))
@coordinates.register(tuple)
@coordinates.register(list)
@coordinates.register(set)
def _(points, grid, snap_fn):
"""Returns coordinates from a sequence of points. Performs minbox
operation on points, thus irregular geometries may be supplied.
Args:
points : a sequence (list, tuple or set) of coordinates.
grid (dict) : {'name': 'chip', 'sx': 3000, 'sy': 3000, 'rx': 1, 'ry': -1}
snap_fn (func): A function that accepts x, y and returns snapped x,y
Returns:
tuple: chip coordinates
Example:
>>> xys = coordinates(((112, 443), (112, 500), (100, 443)),
grid={'sx': 3000, 'sy': 3000},
snap_fn=some_func})
>>> ((100, 500),)
"""
return coordinates(minbox(points), grid=grid, snap_fn=snap_fn)
[docs]def extents(ulx, uly, grid):
"""Given an ulx, uly and grid, returns the grid extents.
ulx and uly are not translated during this calculation.
Args:
ulx (float): 0
uly (float): 0
grid (dict): {'rx', 'ry', 'sx', 'sy'}
Returns:
dict: {'ulx', 'uly', 'lrx', 'lry'}
Example:
>>> extents(ulx=0,
uly=0,
grid={'rx': 1, 'ry': -1, 'sx': 3000, 'sy': 3000}
{'ulx': 0, 'uly': 0, 'lrx': 2999, 'lry': -2999}
"""
return {'ulx': ulx,
'uly': uly,
'lrx': ulx + (grid.get('rx') * grid.get('sx')) - int(np.sign(grid.get('rx'))),
'lry': uly + (grid.get('ry') * grid.get('sy')) - int(np.sign(grid.get('ry')))}