class Orifices(Lines): display_name = ArrayField() sewerage = ArrayField() friction_type = ArrayField() friction_value = ArrayField() crest_type = ArrayField() crest_level = ArrayField() discharge_coefficient_negative = ArrayField() discharge_coefficient_positive = ArrayField() connection_node_start_pk = ArrayField() connection_node_end_pk = ArrayField()
class Pumps(Model): display_name = ArrayField() content_pk = ArrayField() type = ArrayField() node1_id = ArrayField() node2_id = ArrayField() bottom_level = ArrayField() start_level = ArrayField() lower_stop_level = ArrayField() capacity = ArrayField() coordinates = PointArrayField() # coordinates is the centroid of # node_coordinates if both set, else # the one that is set. node_coordinates = LineArrayField() # [ [node1_x], [node1_y], [node2_x], [node2_y]] # Note: -9999 if nodeX_id is -9999 (not set) zoom_category = ArrayField()
class CrossSections(Model): code = ArrayField() shape = ArrayField() content_pk = ArrayField() width_1d = ArrayField() offset = ArrayField() count = ArrayField() tables = ArrayField()
class Manholes(ConnectionNodes): bottom_level = ArrayField() display_name = ArrayField() surface_level = ArrayField() shape = ArrayField() width = ArrayField() manhole_indicator = ArrayField()
class Channels(Lines): code = ArrayField() calculation_type = ArrayField() dist_calc_points = ArrayField() connection_node_start_pk = ArrayField() connection_node_end_pk = ArrayField() discharge_coefficient = ArrayField()
class Levees(Model): crest_level = ArrayField() max_breach_depth = ArrayField() coords = MultiLineArrayField() def __init__(self, *args, **kwargs): super(Levees, self).__init__(*args, **kwargs) self._geoms = [] self.current_epsg = None self._exporters = [ exporters.LeveeOgrExporter(self), ] @property def geoms(self): if not self._geoms: self.load_geoms() return self._geoms def load_geoms(self): """ load levee geometries originating from model DB """ # works on premise of ogr install if ogr is None: raise_import_exception('ogr') if self._geoms: return for line_coords in self.coords: line = ogr.Geometry(ogr.wkbLineString) linepoints = reshape_flat_array(line_coords).T for x in linepoints: line.AddPoint(x[0], x[1]) self._geoms.append(line)
class Breaches(Model): """ fields originating from threedicore: - levbr (breach width) - levmat (material code) - levl (exchange level) - kcu (type field 55 or 56) added fields from spatialite database: - content_pk (primary key database) - seq_ids (sequence ids generated during input file generation) """ content_pk = ArrayField() seq_ids = ArrayField() levbr = ArrayField() levl = IndexArrayField(to='Lines') levmat = ArrayField() kcu = ArrayField() coordinates = PointArrayField() OBJECT_TYPE = constants.TYPE_V2_BREACH def __init__(self, *args, **kwargs): logger.warning( "Deprecation warning: This model is going to be removed " " in the near future, " "please use breaches under lines (lines.breaches) instead" ) super(Breaches, self).__init__(*args, **kwargs) self._exporters = [ exporters.BreachesOgrExporter(self), ]
class EmbeddedNodes(Nodes): embedded_in = ArrayField()
class Nodes(Model): content_pk = ArrayField() seq_id = ArrayField() calculation_type = ArrayField() coordinates = PointArrayField() cell_coords = BboxArrayField() zoom_category = ArrayField() node_type = ArrayField() is_manhole = BooleanArrayField() sumax = ArrayField() drain_level = ArrayField() storage_area = ArrayField() dmax = ArrayField() initial_waterlevel = ArrayField() dimp = ArrayField() SUBSETS = NODE_SUBSETS @property def connectionnodes(self): return self._filter_as(ConnectionNodes, content_pk__ne=0) @property def manholes(self): return self._filter_as(Manholes, is_manhole=True) @property def added_calculationnodes(self): return self._filter_as(AddedCalculationNodes, content_pk=0) def __init__(self, *args, **kwargs): super(Nodes, self).__init__(*args, **kwargs) self._exporters = [ exporters.NodesOgrExporter(self), ] @property def locations_2d(self): data = self.subset('2D_open_water').data # x0 = 0, y0 = 0 # Translate # data['coordinates'][0] += x0 # data['coordinates'][1] += y0 # Return [[node_id, coordinate_x + x0, coordinate_y + y0]] return list(zip( data['id'].tolist(), data['coordinates'][0].tolist(), data['coordinates'][1].tolist()))
class Grid(Model): """ Implemented fields: - nodm: the horizintal index of the cell within its refinement level - nodn: the vertical index of the cell within its refinement level - nodk: the refinement level, 1 being the smallest cell They all have the same size as nodes and cells, which is why this model lives in the nodes/model module. In fact they are attributes of the cell coordinates """ nodm = ArrayField() nodn = ArrayField() nodk = ArrayField() ip = ArrayField() jp = ArrayField() def __init__(self, *args, **kwargs): super(Grid, self).__init__(*args, **kwargs) self.class_kwargs["n2dtot"] = kwargs["n2dtot"] @property def n2dtot(self): return self.class_kwargs["n2dtot"] @property def dx(self): """Return size of the grid cell for each refinement level, in meters. """ return self._datasource["dx"][:] @property def transform(self): """Return the transformation that maps pixel_coords to coordinates. The six returned values (a, b, c, d, e, f) define the (affine) transform between coordinates (x, y) and pixel indices (i, j) as follows:: >>> x = a * i + b * j + c >>> y = d * i + e * j + f Note that for a 3Di grid, the vertical pixel size is positive, while for most raster files this is negative. This means that you should flip the vertical axis of the raster when using the pixel coordinates. """ size = float(self._datasource["dxp"][()]) origin_x = float(self._datasource["x0p"][()]) origin_y = float(self._datasource["y0p"][()]) return size, 0.0, origin_x, 0.0, size, origin_y def get_pixel_map(self, dem_pixelsize, dem_shape): """ get the node grid to pixel map :param dem_pixelsize: pixelsize of the geo tiff :param dem_shape: shape of the numpy representation of the geo tiff :return: flipped array of the dem_shape that matches the geotiff """ # Convert nod_grid to smallest uint type possible dtype = get_smallest_uint_dtype(maxval=self.n2dtot) grid_arr = np.zeros(dem_shape, dtype=dtype) # applies for for 2D nodes only _k = self.nodk[0:self.n2dtot + 1] - 1 _m = self.nodm[0:self.n2dtot + 1] - 1 _n = self.nodn[0:self.n2dtot + 1] - 1 # the size in pixels of each grid cell _size = self.dx[_k] / dem_pixelsize # corresponding node index _ind = np.arange(0, self.n2dtot + 1, dtype='int') n_slice_start = np.array(_n * _size, dtype='int') n_slice_end = np.array((_n * _size) + _size, dtype='int') n_slice = list(zip(n_slice_start, n_slice_end)) m_slice_start = np.array(_m * _size, dtype='int') m_slice_end = np.array((_m * _size) + _size, dtype='int') m_slice = list(zip(m_slice_start, m_slice_end)) for ns, ms, idx in zip(n_slice, m_slice, _ind): axis_x = slice(*ns) axis_y = slice(*ms) grid_arr[axis_x, axis_y] = idx # flip upside-down to match geotiff return grid_arr[::-1, ::]
class Cells(Nodes): """ Model that represents the threedicore calculation cells. ``Cells`` are a sub-class of ``Nodes`` because they share the same attributes. ``Cells``, however, also have a z_coordinate, the bottom level of the grid cell and cell_coords, the lower left and upper right coordinates of the cells extent. """ z_coordinate = ArrayField() pixel_width = ArrayField() pixel_coords = BboxArrayField() has_dem_averaged = ArrayField() def __init__(self, *args, **kwargs): super(Cells, self).__init__(*args, **kwargs) self._exporters = [ exporters.CellsOgrExporter(self), ] @property def bounds(self): """ :return: coordinates of the cell bounds (counter clockwise): minx, miny, maxx, miny, maxx, maxy, minx, maxy """ minx, miny, maxx, maxy = self.cell_coords return np.vstack( (minx, miny, maxx, miny, maxx, maxy, minx, maxy)) def get_id_from_xy(self, x, y, xy_epsg_code=None, subset_name=None): """ :param x: the x coordinate in xy_epsg_code :param y: the y coordinate in xy_epsg_code :param subset_name: filter on a subset of cells :return: numpy array with cell id's for x, y """ if xy_epsg_code and xy_epsg_code != self.epsg_code: xy = transform_xys( np.array([x]), np.array([y]), xy_epsg_code, self.epsg_code).flatten() else: xy = [x, y] inst = self if subset_name: inst = self.subset(subset_name) id = inst.filter(cell_coords__contains_point=xy).id return id.tolist() def get_ids_from_pix_bbox(self, bbox, subset_name='2D_OPEN_WATER'): """ :param x: the x coordinate in xy_epsg_code :param y: the y coordinate in xy_epsg_code :param subset_name: filter on a subset of cells :return: numpy array with cell id's for x, y """ inst = self if subset_name: inst = self.subset(subset_name) id = inst.filter(pixel_coords__intersects_bbox=bbox).id return id.tolist() def get_nodgrid(self, pix_bbox, subset_name='2D_OPEN_WATER'): ids = np.array( self.get_ids_from_pix_bbox(pix_bbox, subset_name=subset_name), dtype=np.int32 ) nodgrid = create_nodgrid( self.pixel_coords[:], ids[:], int(pix_bbox[2] - pix_bbox[0]), int(pix_bbox[3] - pix_bbox[1]), int(pix_bbox[0]), int(pix_bbox[1]) ) return nodgrid[::-1, ::] def get_extent_pixels(self): """Determine the extent of the cells (in pixels) The returned bounding box is left-inclusive; cells cover the the half-open intervals [xmin, xmax) and [ymin, ymax). :return: tuple of xmin, ymin, xmax, ymax or None """ coords = self.pixel_coords mask = ~np.any(coords == -9999, axis=0) if not np.any(mask): return coords = coords[:, mask] xmin = coords[0].min() ymin = coords[1].min() xmax = coords[2].max() ymax = coords[3].max() return xmin, ymin, xmax, ymax def iter_by_tile(self, width, height): """Iterate over groups of cells given a tile shape (in pixels). The tiles are always aligned to pixel (0, 0) so that a single grid cell never overlaps with multiple tiles. For the the same reason, the tile size should be an integer multiple of the maximum cell size. :param width: the width of the tile in pixels :param height: the height of the tile in pixels :yield: (xmin, ymin, xmax, ymax), cells """ # determine the width of the largest cell cell_size = self.pixel_width.max() if width % cell_size != 0 or height % cell_size != 0: raise ValueError( "width and height should be a multiple of {}".format(cell_size) ) # determine the total extent of the 2d nodes xmin, ymin, xmax, ymax = self.get_extent_pixels() # determine the lower left and upper right corners i1 = int(xmin // width) j1 = int(ymin // height) i2 = int(np.ceil(float(xmax) / width)) j2 = int(np.ceil(float(ymax) / height)) # yield the tiles for i, j in itertools.product(range(i1, i2), range(j1, j2)): x1, y1, x2, y2 = ( i * width, j * height, (i + 1) * width, (j + 1) * height, ) result = self.filter( pixel_coords__intersects_bbox=(x1 + 1, y1 + 1, x2 - 1, y2 - 1) ) yield (x1, y1, x2, y2), result def __repr__(self): return "<orm cells instance of {}>".format(self.model_name) def __contenttype__(self): return 'cells'
class ConnectionNodes(Nodes): initial_waterlevel = ArrayField()
class Lines(Model): """ fields originating from threedicore: - kcu (line type) - lik () - line (index of node representing start/end point) added fields from spatialite database: - content_pk (primary key database) - content_type (type of object as stored in database, e.g v2_channel, v2_weir etc) custom fields - line_coords (coordinate pairs start/end point) - line_geometries (geometries from content_type) """ kcu = ArrayField() lik = ArrayField() line = IndexArrayField(to='Nodes') dpumax = ArrayField() flod = ArrayField() flou = ArrayField() cross1 = IndexArrayField(to='CrossSections') cross2 = IndexArrayField(to='CrossSections') ds1d = ArrayField() ds1d_half = ArrayField() cross_weight = ArrayField() invert_level_start_point = ArrayField() invert_level_end_point = ArrayField() content_pk = ArrayField() content_type = ArrayField() zoom_category = ArrayField() cross_pix_coords = LineArrayField() line_coords = LineArrayField() line_geometries = MultiLineArrayField() SUBSETS = LINE_SUBSETS def __init__(self, *args, **kwargs): super(Lines, self).__init__(*args, **kwargs) self._exporters = [ exporters.LinesOgrExporter(self), ] @property def pipes(self): return self._filter_as(Pipes, content_type='v2_pipe') @property def weirs(self): return self._filter_as(Weirs, content_type='v2_weir') @property def orifices(self): return self._filter_as(Orifices, content_type='v2_orifice') @property def channels(self): return self._filter_as(Channels, content_type='v2_channel') @property def culverts(self): return self._filter_as(Culverts, content_type='v2_culvert') @property def breaches(self): return self._filter_as(Breaches, kcu=55) @property def line_nodes(self): return np.dstack(self.line)[0]
class Culverts(Lines): display_name = ArrayField() code = ArrayField() calculation_type = ArrayField() dist_calc_points = ArrayField() cross_section_height = ArrayField() cross_section_width = ArrayField() cross_section_shape = ArrayField() friction_type = ArrayField() friction_value = ArrayField() discharge_coefficient_negative = ArrayField() discharge_coefficient_positive = ArrayField() connection_node_start_pk = ArrayField() connection_node_end_pk = ArrayField()
class Weirs(Lines): code = ArrayField() display_name = ArrayField() discharge_coefficient_negative = ArrayField() discharge_coefficient_positive = ArrayField() sewerage = ArrayField() friction_type = ArrayField() friction_value = ArrayField() crest_type = ArrayField() crest_level = ArrayField() connection_node_start_pk = ArrayField() connection_node_end_pk = ArrayField() cross_section_height = ArrayField() cross_section_width = ArrayField() cross_section_shape = ArrayField() @property def line_coord_angles(self): # Filter raw values with content_type and content_pk # to always get the raw (metric) values # and not the reprojected ones to calculate # the angles with. content_type = self._datasource['content_type'][:] line_content_pk = self._datasource['content_pk'][:] pk_mask = np.isin(line_content_pk, self.content_pk) raw_values = self._datasource['line_coords'][:][ :, (content_type == b'v2_weir') & pk_mask] return self._get_field('line_coords').get_angles_in_degrees( raw_values)
class Pipes(Lines): display_name = ArrayField() friction_type = ArrayField() friction_value = ArrayField() material = ArrayField() cross_section_height = ArrayField() cross_section_width = ArrayField() cross_section_shape = ArrayField() sewerage_type = ArrayField() calculation_type = ArrayField() connection_node_start_pk = ArrayField() connection_node_end_pk = ArrayField() discharge_coefficient = ArrayField()