def get_bounds(content, content_pois, content_areas): """ gets map bounds. first try to get content bounds, last will get content_pois extent """ points = [c.main_location for c in content_pois if c.has_location()] areas = [] for c in content_areas: if hasattr(c, 'location'): if getattr(c.location, 'borders', None): areas.append(c.location.borders) elif getattr(c, 'borders', None): areas.append(c.borders) if points and areas: mp = MultiPoint(points) ap = MultiPolygon(areas) return ap.simplify().union(mp).extent elif len(points) > 1: mp = MultiPoint(points) return mp.extent elif points: # if we have just one point expand the borders # to avoid to much zoom content = content_pois[0] elif areas: ap = MultiPolygon(areas) return ap.extent elif content and content.main_location: return (content.main_location.x - 0.002, content.main_location.y - 0.002, content.main_location.x + 0.002, content.main_location.y + 0.002) else: # default bounding box to avoid google maps failures return (settings.DEFAULT_LONGITUDE - 1.0, settings.DEFAULT_LATITUDE - 1.0, settings.DEFAULT_LONGITUDE + 1.0, settings.DEFAULT_LATITUDE + 1.0)
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%')