Source code for

#!/usr/bin/env python
#-*- coding:utf-8 -*-
# This file is part of the PyNCulture project, which aims at providing tools to
# easily generate complex neuronal cultures.
# Copyright (C) 2017 SENeC Initiative
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <>.

""" Tools for PyNCulture """

import numpy as np

from . import _shapely_support

def indexable(obj):
    Returns true for any iterable which is not a string or byte sequence.
    if hasattr(obj, "__getitem__"):
        return True
    return False

[docs]def pop_largest(shapes): ''' Returns the largest shape, removing it from the list. If `shapes` is a :class:`shapely.geometry.MultiPolygon`, returns the largest :class:`shapely.geometry.Polygon` without modifying the object. .. versionadded:: 0.3 Parameters ---------- shapes : list of :class:`Shape` objects or MultiPolygon. ''' from . import Shape MultiPolygon = Shape try: from shapely.geometry import MultiPolygon except ImportError: pass max_area = -np.inf max_idx = -1 shapes_list = shapes.geoms if isinstance(shapes, MultiPolygon) else shapes for i, s in enumerate(shapes_list): if s.area > max_area: max_area = s.area max_idx = i if isinstance(shapes, MultiPolygon): return shapes.geoms[max_idx] return shapes.pop(max_idx)
def _insert_area(container, area_name, shape, height, properties): ''' Insert the area into the container, potentially restructuring the existing areas. In particular, if the shape is composed of multiple polygons, it will be inserted as "area_name_X", with X ranging from 0 to N-1, N being the number of polygons. If `area_name` already exists in `container`, it will be overriden, potentially even deleted in favor of numbered subareas if it is replaced by a set of polygons. The only exception to that rule is the "default_area", which is never deleted but replaced by the largest polygon, while the smaller ones are numbered from 1. ''' # import from .shape import Area from shapely.geometry import MultiPolygon # check for multiple polygons if isinstance(shape, MultiPolygon): # behavior differs for default_area (never deleted) and other areas if area_name == "default_area": largest = pop_largest(shape) count = len(container.default_areas) for p in shape.geoms: new_name = area_name if p != largest: new_name = area_name + '_' + str(count) count += 1 container._areas[new_name] = Area.from_shape( p, height=height, name=new_name, properties=properties) else: for i, p in enumerate(shape.geoms): new_name = area_name + '_' + str(i) container._areas[new_name] = Area.from_shape( p, height=height, name=new_name, properties=properties) if area_name in container.areas: del container._areas[area_name] else: container._areas[area_name] = Area.from_shape( shape, height=height, name=area_name, properties=properties) def _backup_contains(x, y, shape): try: x = np.array(x) y = np.array(y) except: pass if shape.geom_type == "Disk": x0, y0 = shape.centroid xmin, _, xmax, _ = shape.bounds radius = 0.5*(xmax - xmin) return np.less_equal(np.linalg.norm([x - x0, y - y0], axis=0), radius) elif shape.geom_type == "Ellipse": xmin, ymin, xmax, ymax = shape.bounds a = 0.5*(xmax - xmin) b = 0.5*(ymax - ymin) x0, y0 = shape.centroid return np.less_equal(np.square(x-x0) / a + np.square(y-y0) / b, 1.) elif shape.geom_type == "Rectangle": xmin, ymin, xmax, ymax = shape.bounds contained = np.less_equal(x, xmax) contained *= np.greater_equal(x, xmin) contained *= np.less_equal(y, ymax) contained *= np.greater_equal(y, ymin) return contained raise TypeError("Invalid Shape type: {}.".format(shape.geom_type))