Beispiel #1
0
    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)
Beispiel #2
0
    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)
Beispiel #3
0
    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)
Beispiel #4
0
    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)
Beispiel #5
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%')