def test_get_imixed3(self): """ Test the logic for getting mixed geounits outside a boundary at the lowest geolevel. """ level = self.geolevels[0] bigunits = self.geounits[level.id] boundary = MultiPolygon( Polygon( LinearRing(Point((0, 0)), Point((1, 0)), Point((1, 1)), Point((0, 0))))) boundary.srid = 3785 units = Geounit.get_mixed_geounits( [str(bigunits[3].id), str(bigunits[6].id), str(bigunits[7].id)], self.legbod, level.id, boundary, False) numunits = len(units) # this test should return 3, for the large geounits are completely # without yet intersect at the corner. the net geometry from this # set of mixed geounits is correct, though self.assertEqual( 3, numunits, 'Number of geounits outside boundary is incorrect. (%d)' % numunits) units = Geounit.get_mixed_geounits( [str(bigunits[0].id), str(bigunits[4].id), str(bigunits[8].id)], self.legbod, level.id, boundary, False) numunits = len(units) self.assertEqual( 63, numunits, 'Number of geounits outside boundary is incorrect. (%d)' % numunits)
def test_get_mixed3(self): """ Test the logic for getting mixed geounits inside a boundary at the lowest geolevel. """ level = self.geolevels[0] bigunits = self.geounits[level.id] boundary = MultiPolygon( Polygon( LinearRing(Point((0, 0)), Point((1, 0)), Point((1, 1)), Point((0, 0))))) boundary.srid = 3785 units = Geounit.get_mixed_geounits( [str(bigunits[1].id), str(bigunits[2].id), str(bigunits[5].id)], self.legbod, level.id, boundary, True) numunits = len(units) self.assertEqual( 3, numunits, 'Number of geounits inside boundary is incorrect. (%d)' % numunits) units = Geounit.get_mixed_geounits( [str(bigunits[0].id), str(bigunits[4].id), str(bigunits[8].id)], self.legbod, level.id, boundary, True) numunits = len(units) self.assertEqual( 63, numunits, 'Number of geounits inside boundary is incorrect. (%d)' % numunits)
def test_get_mixed2(self): """ Test the logic for getting mixed geounits inside a boundary at the middle geolevel. """ level = self.geolevels[1] bigunits = self.geounits[level.id] ltlunits = self.geounits[self.geolevels[2].id] boundary = bigunits[0].geom.difference(ltlunits[27].geom) units = Geounit.get_mixed_geounits([str(bigunits[0].id)], self.legbod, level.id, boundary, True) numunits = len(units) self.assertEqual( 8, numunits, 'Number of geounits inside boundary is incorrect. (%d)' % numunits)
def test_get_imixed1(self): """ Test the logic for getting mixed geounits outside a boundary at the highest geolevel. """ level = self.geolevels[0] bigunits = self.geounits[level.id] ltlunits = self.geounits[self.geolevels[1].id] boundary = bigunits[0].geom.difference(ltlunits[9].geom) units = Geounit.get_mixed_geounits([str(bigunits[0].id)], self.legbod, level.id, boundary, False) numunits = len(units) self.assertEqual( 1, numunits, 'Number of geounits outside boundary is incorrect. (%d)' % numunits)
def import_shape(self, store, config, updatefield): """ Import a shapefile, based on a config. Parameters: config -- A dictionary with 'shapepath', 'geolevel', 'name_field', 'region_filters' and 'subject_fields' keys. """ def get_shape_tree(shapefile, feature): shpfields = shapefile.xpath('Fields/Field') builtid = '' for idx in range(0, len(shpfields)): idpart = shapefile.xpath( 'Fields/Field[@type="tree" and @pos=%d]' % idx) if len(idpart) > 0: idpart = idpart[0] part = feature.get(idpart.get('name')) # strip any spaces in the treecode if not (isinstance(part, types.StringTypes)): part = '%d' % part part = part.strip(' ') width = int(idpart.get('width')) builtid = '%s%s' % (builtid, part.zfill(width)) return builtid def get_shape_portable(shapefile, feature): field = shapefile.xpath('Fields/Field[@type="portable"]')[0] portable = feature.get(field.get('name')) if not (isinstance(portable, types.StringTypes)): portable = '%d' % portable return portable def get_shape_name(shapefile, feature): field = shapefile.xpath('Fields/Field[@type="name"]')[0] strname = feature.get(field.get('name')) if type(strname) == str: return strname.decode('latin-1') else: return str(strname) for h, shapefile in enumerate(config['shapefiles']): if not exists(shapefile.get('path')): logger.info( """ ERROR: The filename specified by the configuration: %s Could not be found. Please check the configuration and try again. """, shapefile.get('path')) raise IOError('Cannot find the file "%s"' % shapefile.get('path')) ds = DataSource(shapefile.get('path')) logger.info('Importing from %s, %d of %d shapefiles...', ds, h + 1, len(config['shapefiles'])) lyr = ds[0] logger.info('%d objects in shapefile', len(lyr)) level = Geolevel.objects.get(name=config['geolevel'].lower()[:50]) # Create the subjects we need subject_objects = {} for sconfig in config['subject_fields']: attr_name = sconfig.get('field') foundalias = False for elem in sconfig.getchildren(): if elem.tag == 'Subject': foundalias = True sub = Subject.objects.get( name=elem.get('id').lower()[:50]) if not foundalias: sub = Subject.objects.get( name=sconfig.get('id').lower()[:50]) subject_objects[attr_name] = sub subject_objects['%s_by_id' % sub.name] = attr_name progress = 0.0 logger.info('0% .. ') for i, feat in enumerate(lyr): if (float(i) / len(lyr)) > (progress + 0.1): progress += 0.1 logger.info('%2.0f%% .. ', progress * 100) levels = [level] for region, filter_list in config['region_filters'].iteritems( ): # Check for applicability of the function by examining the config geolevel_xpath = '/DistrictBuilder/GeoLevels/GeoLevel[@name="%s"]' % config[ 'geolevel'] geolevel_config = store.data.xpath(geolevel_xpath) geolevel_region_xpath = '/DistrictBuilder/Regions/Region[@name="%s"]/GeoLevels//GeoLevel[@ref="%s"]' % ( region, geolevel_config[0].get('id')) if len(store.data.xpath(geolevel_region_xpath)) > 0: # If the geolevel is in the region, check the filters for f in filter_list: if f(feat) is True: levels.append( Geolevel.objects.get(name='%s_%s' % (region, level.name))) shape_name = get_shape_name(shapefile, feat) shape_portable_id = get_shape_portable(shapefile, feat) shape_tree_code = get_shape_tree(shapefile, feat) prefetch = Geounit.objects.filter( name=shape_name, geolevel__in=levels, portable_id=shape_portable_id, tree_code=shape_tree_code) should_create = prefetch.count() == 0 if should_create: try: # Store the geos geometry # Buffer by 0 to get rid of any self-intersections which may make this geometry invalid. geos = feat.geom.geos.buffer(0) # Coerce the geometry into a MultiPolygon if geos.geom_type == 'MultiPolygon': my_geom = geos elif geos.geom_type == 'Polygon': my_geom = MultiPolygon(geos) simple = my_geom.simplify(tolerance=Decimal( config['tolerance']), preserve_topology=True) if simple.geom_type != 'MultiPolygon': simple = MultiPolygon(simple) center = my_geom.centroid geos = None # Ensure the centroid is within the geometry if not center.within(my_geom): # Get the first polygon in the multipolygon first_poly = my_geom[0] # Get the extent of the first poly first_poly_extent = first_poly.extent min_x = first_poly_extent[0] max_x = first_poly_extent[2] # Create a line through the bbox and the poly center my_y = first_poly.centroid.y centerline = LineString((min_x, my_y), (max_x, my_y)) # Get the intersection of that line and the poly intersection = centerline.intersection(first_poly) if type(intersection) is MultiLineString: intersection = intersection[0] # the center of that line is my within-the-poly centroid. center = intersection.centroid first_poly = first_poly_extent = min_x = max_x = my_y = centerline = intersection = None g = Geounit(geom=my_geom, name=shape_name, simple=simple, center=center, portable_id=shape_portable_id, tree_code=shape_tree_code) g.save() g.geolevel = levels g.save() except: logger.info('Failed to import geometry for feature %d', feat.fid) logger.info(traceback.format_exc()) continue else: g = prefetch[0] g.geolevel = levels g.save() if not config['attributes']: # If we created a new Geounit, we can let this function know that it doesn't # need to check for existing Characteristics, which will speed things up # significantly. self.set_geounit_characteristic(g, subject_objects, feat, not should_create, updatefield) logger.info('100%') if config['attributes']: progress = 0 logger.info("Assigning subject values to imported geography...") logger.info('0% .. ') for h, attrconfig in enumerate(config['attributes']): if not exists(attrconfig.get('path')): logger.info( """ ERROR: The filename specified by the configuration: %s Could not be found. Please check the configuration and try again. """, attrconfig.get('path')) raise IOError('Cannot find the file "%s"' % attrconfig.get('path')) lyr = DataSource(attrconfig.get('path'))[0] for i, feat in enumerate(lyr): if (float(i) / len(lyr)) > (progress + 0.1): progress += 0.1 logger.info('%2.0f%% .. ', progress * 100) gid = get_shape_tree(attrconfig, feat) g = Geounit.objects.filter(tree_code=gid) if g.count() > 0: self.set_geounit_characteristic( g[0], subject_objects, feat, True, updatefield) logger.info('100%')