Example #1
0
def nyc_boundary():
    boroughs = {}
    # maps BoroCode to categorical
    borough_names = {5: 'SI', 4: 'Q', 3: 'BK', 1: 'M', 2: 'BX'}

    with fiona.open('data/indata/nybb_13a/nybb.shp', 'r') as source:
        # set up three projection types: boundary data start; google-standard
        # lat/lon, using WGS84; Albers Equal Area
        p1 = Proj(source.crs, preserve_units=True)
        p2 = Proj({'proj': 'longlat', 'datum': 'WGS84'})
        p3 = Proj({'proj': 'aea', 'datum': 'WGS84', 'lon_0': '-96'})

        # for each shape, convert its coordinates to AEA
        nyc = MultiPolygon()
        for borough in source:
            borough_shapes = MultiPolygon()
            borocode = borough['properties']['BoroCode']
            for shape in borough['geometry']['coordinates']:
                p1_points = np.array(shape[0])

                p2_points = transform(p1, p2, p1_points[:, 0], p1_points[:, 1])
                p3_points = transform(p2, p3, p2_points[0], p2_points[1])
                p3_points = np.vstack([p3_points[0], p3_points[1]]).T

                new = Polygon(p3_points)
                nyc = nyc.union(new)
                borough_shapes = borough_shapes.union(new)
            boroughs[borocode] = borough_shapes

    # i = 0
    # for shape in nyc:
    # x,y = shape.exterior.xy
    # plt.plot(x,y)

    # bounds = np.array(shape.bounds)
    # bounds = bounds.reshape([2,2])
    # cx = np.mean(bounds[:,0])
    # cy = np.mean(bounds[:,1])

    # plt.text(cx,cy,str(i))
    # i = i+1
    # By running the above, we identify the following islands that contain subway stations:
    #        i  borough            name
    #   1.   3  staten island      staten island
    #   2.  39  manhattan          manhattan
    #   3.  36  manhattan          roosevelt island
    #   4.  40  bronx              bronx
    #   5.  10  brooklyn / queens  rockaways
    #   6.  14  brooklyn / queens  jamaica bay wildlife refuge / broad channel
    #   7.  15  brooklyn / queens  brooklyn / queens
    # indices = [3, 39, 36, 40, 10, 14, 15]
    indices = [39, 36, 40, 10, 14, 15]
    boundary = []
    for i in indices:
        boundary.append(nyc[i])
    nyc = MultiPolygon(boundary)

    return nyc, boroughs, borough_names
Example #2
0
def _make_polygon(gpd_obj):
    """select geometry which are from the type Polygon, MultiPolygon or
     GeometryCollection. The last one is converted to a Polygon/Multipolygon
     (Line Strings/MultiLineStrings are removed)

    Parameters
    ----------
    gpd_obj :   gpd.GeoDataFrame

    Returns
    -------
    gpd.GeoDataFrame, that only contains Polygons or MultiPolygons
    """

    gpd_obj = gpd_obj[(gpd_obj.type == 'GeometryCollection') |
                      (gpd_obj.type == 'Polygon') |
                      (gpd_obj.type == 'MultiPolygon')]
    if not gpd_obj.empty:
        collection = gpd_obj[(~gpd_obj.is_empty) &
                             (gpd_obj.type == 'GeometryCollection')]
        # choose only polygons or multipolygons
        for c in collection.index:
            geo = collection.loc[c, 'geometry']
            new = MultiPolygon()
            for obj in geo:
                if obj.type in ['Polygon', 'MultiPolygon']:
                    new = new.union(obj)
            gpd_obj = gpd_obj.copy()
            gpd_obj.loc[c, 'geometry'] = new
    return gpd_obj
Example #3
0
def flowshed_calculation(dem, shp):
    """ calculate flowsheds

    Parameters
    ----------
    dem : str
        path to a raster file
    shp : str
        path to a shape file

    Returns
    -------
    path to the shape file containing all the flowsheds
    (shapely.geometry.Polygon)
    """
    dir = os.path.dirname(dem)
    watershed_dir = os.path.join(dir, 'all_watersheds.shp')
    flowshed_dir = os.path.join(dir, 'flowshed.shp')
    # calculate watersheds
    routing.delineate_watershed(dem, shp, 1, 100, watershed_dir,
                                os.path.join(dir, 'snapped_outlet_points.shp'),
                                os.path.join(dir, 'stream_out_uri.tif'))
    watersheds = gpd.read_file(watershed_dir).buffer(0)
    pour_points = gpd.read_file(dir + '/pour_points.shp')
    flowsheds = gpd.GeoSeries(watersheds.intersection(co), crs=crs)
    # remove empty objects

    flowsheds = flowsheds[(~flowsheds.is_empty) & (flowsheds.type != 'Point')
                          & (flowsheds.type != 'MultiPoint')
                          & (flowsheds.type != 'LineString')
                          & (flowsheds.type != 'MultiLineString')]
    collections = flowsheds[flowsheds.type == 'GeometryCollection']
    for index in collections.index:
        multi = MultiPolygon()
        for geo in collections.loc[index]:
            if geo.type is 'Polygon':
                multi = multi.union(geo)
        flowsheds.loc[index] = multi
    # if object is Multipolygon split it
    for i, shed in enumerate(flowsheds):
        if shed.type is 'MultiPolygon':
            # find polygon with minimal distance to pour point
            dist = []
            for s0 in shed:
                dist.append(s0.distance(pour_points.loc[i, 'geometry']))
            # add each polygon to all_watershed.shp
            for j, s1 in enumerate(shed):
                # polygon nearest to PP get current id
                if j == np.argmin(dist):
                    flowsheds.loc[i] = shape(s1)
                # all other poylgons were added at the end
                else:
                    s3 = gpd.GeoSeries(s1)
                    flowsheds = flowsheds.append(s3, ignore_index=True)
    result = gpd.GeoDataFrame(geometry=flowsheds)
    result.to_file(flowshed_dir)
    return flowshed_dir
def iou(target_bboxes, predicted_bboxes):
    target_polys = MultiPolygon([Polygon(i) for i in target_bboxes]).buffer(0)
    predicted_polys = MultiPolygon([Polygon(i)
                                    for i in predicted_bboxes]).buffer(0)
    intersection = target_polys.intersection(predicted_polys).area
    union = target_polys.union(predicted_polys).area

    try:
        return intersection / union
    except ZeroDivisionError as e:
        return 0
Example #5
0
def is_subset_polygon(left: geometry.MultiPolygon,
                      right: geometry.MultiPolygon) -> bool:
    area_union = left.union(right).area
    area_intersection = left.intersection(right).area
    area_left = left.area
    area_right = right.area

    result = math.isclose(area_intersection, area_right, abs_tol=0.03**2)

    jaccard_expected = (area_left - area_right) / area_left
    jaccard_actual = (area_union - area_intersection) / area_union

    if DEBUG:
        logger.debug(f"left, right -> {area_left}, {area_right}")
        logger.debug(
            f"Je = ({area_left:.2f} - {area_right:.2f}) / {area_left:.2f} = {jaccard_expected:.2f}"
        )
        logger.debug(
            f"Ja = ({area_union:.2f} - {area_intersection:.2f}) / {area_union:.2f} = {jaccard_actual:.2f}"
        )

        logger.debug(f"{jaccard_expected} == {jaccard_actual} -> {result}")
    result = math.isclose(jaccard_expected, jaccard_actual, abs_tol=0.1)
    return result
Example #6
0
class path_planner(Node):
    def __init__(self):
        super().__init__("wps")
        self.profile = qos_profile_sensor_data
        self.im_subscriber = self.create_subscription(
            Image,
            "/zenith_camera/image_raw",
            self.im_callback,
            qos_profile=self.profile)
        self.obj_subscriber = self.create_subscription(
            Pose2D, "/objectif", self.obj_callback, qos_profile=self.profile)
        self.p1_subscriber = self.create_subscription(Pose2D,
                                                      "/player1Pose",
                                                      self.obstacles_callback,
                                                      qos_profile=self.profile)
        self.p2_subscriber = self.create_subscription(Pose2D,
                                                      "/player2Pose",
                                                      self.obstacles_callback,
                                                      qos_profile=self.profile)

        self.wps_pub = self.create_publisher(Float64MultiArray, "/waypoints",
                                             self.profile)

        self.tfBuffer = tf2_ros.Buffer()
        self.listener = tf2_ros.TransformListener(self.tfBuffer, self)

        self.l_rob = 1.5
        self.obstacles_fixes()
        self.terrain = Polygon(rect_from_center(HEIGHT,
                                                WIDHT)).buffer(-self.l_rob)
        self.small_terrain = Polygon(rect_from_center(HEIGHT,
                                                      WIDHT)).buffer(-3)

        self.pos = (-5, 0)
        self.path = LineString(((0, 0), (0, 0)))
        self.polys = MultiPolygon()
        self.saved_obs
        self.i_player = 0

    def obstacles_fixes(self):

        l_box, L_box = 2.3, 2.7

        l_filet, L_filet = 13, 0.3

        l_mur, e_mur = 0.1, 0.5
        mur1 = rect_from_center(l_mur, e_mur, 5.7, 14.5)
        mur2 = rect_from_center(e_mur, l_mur, 7.5, 12.5)
        mur3 = rect_from_center(l_mur, e_mur, -5.7, -14.5)
        mur4 = rect_from_center(e_mur, l_mur, -7.5, -12.5)

        # cout("{}, {}".format(WIDHT/2 - l_box, HEIGHT/2 - L_box))

        self.obstacles = MultiPolygon([
            Polygon(mur1),
            Polygon(mur2),
            Polygon(mur3),
            Polygon(mur4),
            # Polygon(rect(l_box, L_box, -WIDHT/2, -HEIGHT/2)),
            # Polygon(rect(l_box, L_box, WIDHT/2 - l_box, HEIGHT/2 - L_box)),
            Polygon(rect_from_center(l_filet, L_filet))
        ]).buffer(self.l_rob)

        self.saved_obs = self.obstacles

    def rec_path(self, A, B, d, sens=0):
        # print("d : ", d)
        line = LineString((A, B))
        # if d>5:
        #     return (A, B), d
        if not self.terrain.contains(line):
            return None, 0

        for obs in self.obstacles:
            if line.crosses(obs):
                coords = (obs.union(line)).convex_hull.exterior.coords
                i = list(coords).index(A)
                if sens != 0:
                    #print("AHHHH ! : ", (i+sens)%len(list(coords)))
                    C = coords[(i + sens) % len(list(coords))]
                    # print("C1, C2 : ", C1, C2)
                    pts, d0 = self.rec_path(C, B, d, sens=sens)
                    if pts == None:
                        return None, 0
                    d += d0
                else:
                    C1, C2 = coords[i + 1], coords[(i - 2) % len(list(coords))]

                    # print("C1, C2 : ", C1, C2)
                    pts1, d1 = self.rec_path(C1, B, d, sens=1)
                    pts2, d2 = self.rec_path(C2, B, d, sens=-2)
                    if d1 > d2 or pts1 == None:
                        C = C2
                        pts = pts2
                        d += d2
                        if pts == None:
                            return None, 0
                    else:
                        C = C1
                        pts = pts1
                        d += d1

                d += cdist(
                    np.array(A).reshape(1, -1),
                    np.array(C).reshape(1, -1))
                pts.insert(0, A)
                return pts, d

        d += cdist(np.array(A).reshape(1, -1), np.array(B).reshape(1, -1))
        return [A, B], d

    def im_callback(self, msg):

        if DEBUG:
            bridge = cv_bridge.CvBridge()
            cv_im = bridge.imgmsg_to_cv2(msg, desired_encoding='bgr8')

            # plot obstacle et safe_zones

            for obs in self.obstacles.geoms:
                pts = world_to_img(obs.exterior.coords).reshape((-1, 1, 2))
                cv2.polylines(cv_im, [pts], True, (0, 0, 255))

                # if self.path.crosses(obs):
                #     coords = (obs.union(self.path)).convex_hull.exterior.coords
                #     coords = world_to_img(coords)
                #     cv2.polylines(cv_im,[coords],True,(255,0,255))

            # for poly in self.polys.geoms:
            #     pts = world_to_img(poly.exterior.coords).reshape((-1,1,2))
            #     cv2.polylines(cv_im,[pts],True,(255,0,255))

            pts = world_to_img(self.terrain.exterior.coords).reshape(
                (-1, 1, 2))
            cv2.polylines(cv_im, [pts], True, (0, 255, 0))

            im_path = world_to_img(self.path.coords)

            for i in range(1, im_path.shape[0]):
                # cv2.line(cv_im, self.path[i-1, :], self.path[i, :], (0, 255, 255), thickness=5)
                cv2.line(cv_im, (im_path[i - 1, 0], im_path[i - 1, 1]),
                         (im_path[i, 0], im_path[i, 1]), (0, 255, 255),
                         thickness=3)

            cv2.imshow("cv_im", cv_im)
            cv2.waitKey(1)

    def obj_callback(self, msg):
        A = (msg.x, msg.y)
        transform = self.tfBuffer.lookup_transform('odom', 'base_link',
                                                   tf2_ros.Time())
        self.pos = (transform.transform.translation.x,
                    transform.transform.translation.y)

        if not self.terrain.contains(Point(A)):

            cout("Objectif not in safe zone")

            #box = Point(A).buffer(2*self.l_rob)
            box = Polygon(
                rect_from_center(3 * self.l_rob, 3 * self.l_rob, A[0], A[1]))
            self.polys = self.polys.union(box)

            #pts, d = self.rec_path(self.pos, A, 0)
            I = self.terrain.exterior.intersection(box.exterior)
            if len(I.geoms) == 2:
                B = I.geoms[0]
                C = I.geoms[1]
                if cdist(
                        np.array(self.pos).reshape(1, -1),
                        np.array(B).reshape(1, -1)) > cdist(
                            np.array(self.pos).reshape(1, -1),
                            np.array(C).reshape(1, -1)):
                    B, C = C, B

                pts, d1 = self.rec_path(self.pos, B, 0)
                if pts != None:
                    pts.append(C)
                    self.path = LineString(pts)
                    self.send_wps()

            # if pts != None:
            #     I = self.terrain.exterior.intersection(box.exterior)
            #     for B in I:
            #         cout(B)
            #     self.path = LineString(pts)
        else:
            pts, d = self.rec_path(self.pos, A, 0)
            if pts != None:
                self.path = LineString(pts)
                self.send_wps()

    def obstacles_callback(self, msg):

        dalpha = np.pi / 6
        r = 4
        x, y, theta = msg.x, msg.y, msg.theta
        A, B = (x + r * np.cos(theta + dalpha), y +
                r * np.sin(theta + dalpha)), (x + r * np.cos(theta - dalpha),
                                              y + r * np.sin(theta - dalpha))

        poly = Polygon((A, B, (x, y))).intersection(self.small_terrain)

        if self.i_player < 2:
            self.obstacles = self.obstacles.union(poly)
            self.i_player += 1
        else:
            self.obstacles = self.saved_obs.union(poly)
            self.i_player = 0

    def send_wps(self):
        data_to_send = Float64MultiArray(
        )  # the data to be sent, initialise the array
        data_to_send.data = [1.0, 2.0]
        data_to_send.data = list(np.array(self.path.coords).flatten(
        ))  # assign the array with the value you want to send
        self.wps_pub.publish(data_to_send)
Example #7
0
    p1 = Proj(source.crs,preserve_units=True)
    p2 = Proj({'proj':'longlat', 'datum':'WGS84'})
    p3 = Proj({'proj':'aea', 'datum':'WGS84', 'lon_0':'-96'})

    # for each shape, convert its coordinates to AEA
    nyc = MultiPolygon()
    for shape in source:
        for subshape in shape['geometry']['coordinates']:
            p1_points = np.array(subshape[0])

            p2_points = transform(p1, p2, p1_points[:,0], p1_points[:,1])
            p3_points = transform(p2, p3, p2_points[0], p2_points[1])
            p3_points = np.vstack([p3_points[0], p3_points[1]]).T

            new = Polygon(p3_points)
            nyc = nyc.union(new)

# i = 0
# for shape in nyc:
    # x,y = shape.exterior.xy
    # plt.plot(x,y)

    # bounds = np.array(shape.bounds)
    # bounds = bounds.reshape([2,2])
    # cx = np.mean(bounds[:,0])
    # cy = np.mean(bounds[:,1])

    # plt.text(cx,cy,str(i))
    # i = i+1
# By running the above, we identify the following islands that contain subway stations:
#        i  borough            name
Example #8
0
sectorkeys = sorted(sectors.iterkeys())

whole1 = sectors[sectorkeys[0]]['1']
whole2 = sectors[sectorkeys[0]]['2']
if whole1 == None:
    whole1 = MultiPolygon()
if whole2 == None:
    whole2 = MultiPolygon()

counter = 0
curx = 0
curstrip1 = Polygon()
curstrip2 = Polygon()
for s in sectorkeys[1:]:
    if s[0] > curx:
        whole1 = whole1.union(curstrip1)
        whole2 = whole2.union(curstrip2)
        curx = s[0]
        curstrip1 = Polygon()
        curstrip2 = Polygon()

    print "-->" + str(s)
    if sectors[s]['1']:
        curstrip1 = curstrip1.union(sectors[s]['1'])
    if sectors[s]['2']:
        curstrip2 = curstrip2.union(sectors[s]['2'])
    #counter+=1
    #if counter > 100:
    #  break

whole1.buffer(1.0)
Example #9
0
def DiagnoseAndFixMultiPolygon(attributeName,
                               boundariesList,
                               printDiagnostic=False):
    print "---"
    print "*** %s is invalid. Running diagnostic... ***" % (attributeName)
    # Note: we have already tested each polygon, so the problem is when we add them to a multipolygon
    # the likely cause is overlapping polygons, so we will automatically union those polygons
    #printDiagnostic = True
    goodPolygons = []
    badPolygons = []
    useSlowerCode = False
    numPolygons = len(boundariesList)
    if useSlowerCode:
        for i in range(0, numPolygons):
            if i % 20 == 0:
                print "processing polygon", i
            thisPolygon = boundariesList[i]
            shapelyMultiPoly = MultiPolygon(goodPolygons + [thisPolygon])
            if shapelyMultiPoly.is_valid:
                goodPolygons.append(thisPolygon)
            else:
                print "*** problem when adding polygon %d ***" % (i)
                print thisPolygon
                badPolygons.append(thisPolygon)
    else:
        ''' it can be very slow trying these one at a time, 
        so lets try jumping when we can and dropping back to one at a time when we have a problem
        '''
        numPolygons = len(boundariesList)
        if printDiagnostic: print "numPolygons", numPolygons

        i = 0
        lineReported = 0
        while True:
            if i >= numPolygons:
                if printDiagnostic: print "reached end of list"
                break  # end of list

            lineToReport = (i / 100) * 100
            if lineToReport != lineReported:
                lineReported = lineToReport
                if i > 0:
                    print "processing polygon %d of %d" % (i, numPolygons)

            continueToTop = False
            for numToJump in [100, 50, 20, 10]:
                if continueToTop: continue
                if i + numToJump > numPolygons:
                    numToJump = numPolygons - i
                    if printDiagnostic:
                        print "adjusting numToJump to", numToJump
                #try the jump
                if printDiagnostic: print "trying jump"
                polygonsToAdd = boundariesList[i:i + numToJump]
                shapelyMultiPoly = MultiPolygon(goodPolygons + polygonsToAdd)
                if shapelyMultiPoly.is_valid:
                    goodPolygons = goodPolygons + polygonsToAdd
                    i = i + numToJump
                    if printDiagnostic: print "jumped to %i" % (i)
                    continueToTop = True

            if continueToTop: continue

            #resort to one at a time
            if printDiagnostic:
                print "resorting to one at a time. i = %d" % (i)
            for j in range(0, numToJump):
                thisPolygon = boundariesList[i]
                shapelyMultiPoly = MultiPolygon(goodPolygons + [thisPolygon])
                if shapelyMultiPoly.is_valid:
                    goodPolygons.append(thisPolygon)
                else:
                    if True: print "polygon %d is bad" % (i)
                    badPolygons.append(thisPolygon)
                i = i + 1

    # now automatically generate a "fixed" multipolygon by unioning the bad polygons
    shapelyMultiPoly = MultiPolygon(goodPolygons)
    if len(badPolygons) > 0:
        print "---"
        print "*** Handling bad polygons ***"
        for poly in badPolygons:
            print "Bad polygon:", poly
            islandPoly = MultiPolygon([poly])
            if islandPoly.is_valid:
                print "Fixed: The bad polygon was a valid polygon, it has been unioned to the whole."
                shapelyMultiPoly = shapelyMultiPoly.union(islandPoly)
            else:
                # try to fix this polygon
                '''
                Outer boundary problems:
                Sometimes with clipping of shoreline, there are cases of the outer boundary of the
                shoreline crossing itself.  There is not really anything we can do about such cases.
                '''
                # check to see if the outer boundary is valid
                outerBoundary, innerBoundaries = poly

                islandPoly = MultiPolygon([(outerBoundary, [])])
                if not islandPoly.is_valid:
                    print "Outer boundary is not valid."
                    print "Unable to fix this polygon."
                    if attributeName == "MAPLAND":
                        print "This is a minor error, it just means the oil contours will not be clipped to this part of the land."
                    else:
                        print "This is a serious error.  Part of the %s area will be missing." % (
                            attributeName)
                    print "---"
                    continue  # we will omit this polygon

                print "The outer boundary of the polygon is valid."

                ##############################
                '''
                Hole problems:
                There are two kinds of problems that can occur with GNOME Analyst contours.
                
                Sometimes the holes stick slightly out of the outerboundary.
                In such a case we will rely on the fact that the holes were just areas to be removed.
                
                Sometimes there seem to be holes within holes. 
                I'm not sure why GNOME Analyst is doing that, but we will assume that 
                the holes were just areas to be removed.
                '''
                numHoles = len(innerBoundaries)
                if numHoles > 0: print "Examing the %d holes..." % (numHoles)
                assert numHoles > 0  # the only way to get to this part of the code is for a hole to be causing the problem
                numOmittedHoles = 0
                for innerBoundary in innerBoundaries:
                    hole = Polygon(innerBoundary)
                    if not hole.is_valid:
                        numOmittedHoles = numOmittedHoles + 1
                        print "Hole is not valid:", hole
                        print "Omitting this hole. This is a minor error."
                    else:
                        # subtract this hole from the islandPolygon
                        islandPoly = islandPoly.difference(hole)

                if numOmittedHoles > 0:
                    print "Partially fixed: %d invalid holes were not subtracted from this polygon, but this polygon has been unioned to the whole." % (
                        numOmittedHoles)
                elif numHoles > 0:
                    print "Fixed: all holes successfully subtracted from this polygon and the polygon unioned to the whole."
                else:
                    print "Fixed: polygon unioned to the whole."

                shapelyMultiPoly = shapelyMultiPoly.union(islandPoly)

            print "---"

    return shapelyMultiPoly
Example #10
0
    p1 = Proj(source.crs, preserve_units=True)
    p2 = Proj({'proj': 'longlat', 'datum': 'WGS84'})
    p3 = Proj({'proj': 'aea', 'datum': 'WGS84', 'lon_0': '-96'})

    # for each shape, convert its coordinates to AEA
    nyc = MultiPolygon()
    for shape in source:
        for subshape in shape['geometry']['coordinates']:
            p1_points = np.array(subshape[0])

            p2_points = transform(p1, p2, p1_points[:, 0], p1_points[:, 1])
            p3_points = transform(p2, p3, p2_points[0], p2_points[1])
            p3_points = np.vstack([p3_points[0], p3_points[1]]).T

            new = Polygon(p3_points)
            nyc = nyc.union(new)

# i = 0
# for shape in nyc:
# x,y = shape.exterior.xy
# plt.plot(x,y)

# bounds = np.array(shape.bounds)
# bounds = bounds.reshape([2,2])
# cx = np.mean(bounds[:,0])
# cy = np.mean(bounds[:,1])

# plt.text(cx,cy,str(i))
# i = i+1
# By running the above, we identify the following islands that contain subway stations:
#        i  borough            name
Example #11
0
    def custom_render(self, level_render_data, access_permissions, full_levels):
        if full_levels:
            levels = get_full_levels(level_render_data)
        else:
            levels = get_main_levels(level_render_data)

        buildings = None
        areas = None

        main_building_block = None
        main_building_block_diff = None

        current_upper_bound = None
        for geoms in levels:
            # hide indoor and outdoor rooms if their access restriction was not unlocked
            restricted_spaces_indoors = unary_union(
                tuple(area.geom for access_restriction, area in geoms.restricted_spaces_indoors.items()
                      if access_restriction not in access_permissions)
            )
            restricted_spaces_outdoors = unary_union(
                tuple(area.geom for access_restriction, area in geoms.restricted_spaces_outdoors.items()
                      if access_restriction not in access_permissions)
            )
            restricted_spaces = unary_union((restricted_spaces_indoors, restricted_spaces_outdoors))  # noqa

            # crop altitudeareas
            for altitudearea in geoms.altitudeareas:
                altitudearea.geometry = altitudearea.geometry.geom.difference(restricted_spaces)
                altitudearea.geometry_prep = prepared.prep(altitudearea.geometry)

            # crop heightareas
            new_heightareas = []
            for geometry, height in geoms.heightareas:
                geometry = geometry.geom.difference(restricted_spaces)
                geometry_prep = prepared.prep(geometry)
                new_heightareas.append((geometry, geometry_prep, height))
            geoms.heightareas = new_heightareas

            if geoms.on_top_of_id is None:
                buildings = geoms.buildings
                areas = MultiPolygon()
                current_upper_bound = geoms.upper_bound

                holes = geoms.holes.difference(restricted_spaces)
                buildings = buildings.difference(holes)
                areas = areas.union(holes.buffer(0).buffer(0.01, join_style=JOIN_STYLE.mitre))

                main_building_block = OpenScadBlock('union()', comment='Level %s' % geoms.short_label)
                self.root.append(main_building_block)
                main_building_block_diff = OpenScadBlock('difference()')
                main_building_block.append(main_building_block_diff)
                main_building_block_inner = OpenScadBlock('union()')
                main_building_block_diff.append(main_building_block_inner)
                main_building_block_inner.append(
                    self._add_polygon(None, buildings.intersection(self.bbox), geoms.lower_bound, geoms.upper_bound)
                )

            for altitudearea in sorted(geoms.altitudeareas, key=attrgetter('altitude')):
                if not altitudearea.geometry.intersects(self.bbox):
                    continue

                if altitudearea.altitude2 is not None:
                    name = 'Altitudearea %s-%s' % (altitudearea.altitude/1000, altitudearea.altitude2/1000)
                else:
                    name = 'Altitudearea %s' % (altitudearea.altitude / 1000)

                # why all this buffering?
                # buffer(0) ensures a valid geometry, this is sadly needed sometimes
                # the rest of the buffering is meant to make polygons overlap a little so no glitches appear
                # the intersections below will ensure that they they only overlap with each other and don't eat walls
                geometry = altitudearea.geometry.buffer(0)
                inside_geometry = geometry.intersection(buildings).buffer(0).buffer(0.01, join_style=JOIN_STYLE.mitre)
                outside_geometry = geometry.difference(buildings).buffer(0).buffer(0.01, join_style=JOIN_STYLE.mitre)
                geometry_buffered = geometry.buffer(0.01, join_style=JOIN_STYLE.mitre)
                if geoms.on_top_of_id is None:
                    areas = areas.union(geometry)
                    buildings = buildings.difference(geometry).buffer(0)
                    inside_geometry = inside_geometry.intersection(areas).buffer(0)
                    outside_geometry = outside_geometry.intersection(areas).buffer(0)
                    geometry_buffered = geometry_buffered.intersection(areas).buffer(0)
                outside_geometry = outside_geometry.intersection(self.bbox)

                if not inside_geometry.is_empty:
                    if altitudearea.altitude2 is not None:
                        min_slope_altitude = min(altitudearea.altitude, altitudearea.altitude2)
                        max_slope_altitude = max(altitudearea.altitude, altitudearea.altitude2)
                        bounds = inside_geometry.bounds

                        # cut in
                        polygon = self._add_polygon(None, inside_geometry,
                                                    min_slope_altitude-10, current_upper_bound+1000)

                        slope = self._add_slope(bounds, altitudearea.altitude, altitudearea.altitude2,
                                                altitudearea.point1, altitudearea.point2, bottom=True)
                        main_building_block_diff.append(
                            OpenScadBlock('difference()', children=[polygon, slope], comment=name+' inside cut')
                        )

                        # actual thingy
                        if max_slope_altitude > current_upper_bound and inside_geometry.intersects(self.bbox):
                            polygon = self._add_polygon(None, inside_geometry.intersection(self.bbox),
                                                        current_upper_bound-10, max_slope_altitude+10)
                            slope = self._add_slope(bounds, altitudearea.altitude, altitudearea.altitude2,
                                                    altitudearea.point1, altitudearea.point2, bottom=False)
                            main_building_block.append(
                                OpenScadBlock('difference()',
                                              children=[polygon, slope], comment=name + 'outside')
                            )
                    else:
                        if altitudearea.altitude < current_upper_bound:
                            main_building_block_diff.append(
                                self._add_polygon(name+' inside cut', inside_geometry,
                                                  altitudearea.altitude, current_upper_bound+1000)
                            )
                        else:
                            main_building_block.append(
                                self._add_polygon(name+' inside', inside_geometry.intersection(self.bbox),
                                                  min(altitudearea.altitude-700, current_upper_bound-10),
                                                  altitudearea.altitude)
                            )

                if not outside_geometry.is_empty:
                    if altitudearea.altitude2 is not None:
                        min_slope_altitude = min(altitudearea.altitude, altitudearea.altitude2)
                        max_slope_altitude = max(altitudearea.altitude, altitudearea.altitude2)
                        bounds = outside_geometry.bounds

                        polygon = self._add_polygon(None, outside_geometry,
                                                    min_slope_altitude-710, max_slope_altitude+10)
                        slope1 = self._add_slope(bounds, altitudearea.altitude, altitudearea.altitude2,
                                                 altitudearea.point1, altitudearea.point2, bottom=False)
                        slope2 = self._add_slope(bounds, altitudearea.altitude-700, altitudearea.altitude2-700,
                                                 altitudearea.point1, altitudearea.point2, bottom=True)
                        union = OpenScadBlock('union()', children=[slope1, slope2], comment=name+'outside')
                        main_building_block.append(
                            OpenScadBlock('difference()',
                                          children=[polygon, union], comment=name+'outside')
                        )
                    else:
                        if geoms.on_top_of_id is None:
                            lower = geoms.lower_bound
                        else:
                            lower = altitudearea.altitude-700
                            if lower == current_upper_bound:
                                lower -= 10
                        main_building_block.append(
                            self._add_polygon(name+' outside', outside_geometry, lower, altitudearea.altitude)
                        )

                # obstacles
                if altitudearea.altitude2 is not None:
                    obstacles_diff_block = OpenScadBlock('difference()', comment=name + ' obstacles')
                    had_obstacles = False

                    obstacles_block = OpenScadBlock('union()')
                    obstacles_diff_block.append(obstacles_block)

                    min_slope_altitude = min(altitudearea.altitude, altitudearea.altitude2)
                    max_slope_altitude = max(altitudearea.altitude, altitudearea.altitude2)
                    bounds = geometry.bounds

                    for height, obstacles in altitudearea.obstacles.items():
                        height_diff = OpenScadBlock('difference()')
                        had_height_obstacles = None

                        height_union = OpenScadBlock('union()')
                        height_diff.append(height_union)

                        for obstacle in obstacles:
                            if not obstacle.geom.intersects(self.bbox):
                                continue
                            obstacle = obstacle.geom.buffer(0).buffer(0.01, join_style=JOIN_STYLE.mitre)
                            if self.min_width:
                                obstacle = obstacle.union(self._satisfy_min_width(obstacle)).buffer(0)
                            obstacle = obstacle.intersection(geometry_buffered)
                            if not obstacle.is_empty:
                                had_height_obstacles = True
                                had_obstacles = True
                            height_union.append(
                                self._add_polygon(None, obstacle.intersection(self.bbox),
                                                  min_slope_altitude-20, max_slope_altitude+height+10)
                            )

                        if had_height_obstacles:
                            obstacles_block.append(height_diff)
                            height_diff.append(
                                self._add_slope(bounds, altitudearea.altitude+height, altitudearea.altitude2+height,
                                                altitudearea.point1, altitudearea.point2, bottom=False)
                            )

                    if had_obstacles:
                        main_building_block.append(obstacles_diff_block)
                        obstacles_diff_block.append(
                            self._add_slope(bounds, altitudearea.altitude-10, altitudearea.altitude2-10,
                                            altitudearea.point1, altitudearea.point2, bottom=True)
                        )
                else:
                    obstacles_block = OpenScadBlock('union()', comment=name + ' obstacles')
                    had_obstacles = False
                    for height, obstacles in altitudearea.obstacles.items():
                        for obstacle in obstacles:
                            if not obstacle.geom.intersects(self.bbox):
                                continue
                            obstacle = obstacle.geom.buffer(0).buffer(0.01, join_style=JOIN_STYLE.mitre)
                            if self.min_width:
                                obstacle = obstacle.union(self._satisfy_min_width(obstacle)).buffer(0)
                            obstacle = obstacle.intersection(geometry_buffered).intersection(self.bbox)
                            if not obstacle.is_empty:
                                had_obstacles = True
                            obstacles_block.append(
                                self._add_polygon(None, obstacle,
                                                  altitudearea.altitude-10, altitudearea.altitude+height)
                            )

                    if had_obstacles:
                        main_building_block.append(obstacles_block)

            if self.min_width and geoms.on_top_of_id is None:
                main_building_block_inner.append(
                    self._add_polygon('min width',
                                      self._satisfy_min_width(buildings).intersection(self.bbox).buffer(0),
                                      geoms.lower_bound, geoms.upper_bound)
                )
Example #12
0
def gen_macros_for_non_fence_region(macro_pos_x,
                                    macro_pos_y,
                                    macro_size_x,
                                    macro_size_y,
                                    regions,
                                    yl,
                                    yh,
                                    merge=False,
                                    plot=False):
    # tt = time.time()
    macros = MultiPolygon([
        box(
            macro_pos_x[i],
            macro_pos_y[i],
            macro_pos_x[i] + macro_size_x[i],
            macro_pos_y[i] + macro_size_y[i],
        ) for i in range(macro_size_x.size(0))
    ])
    # print("macro:", time.time()-tt)

    # tt = time.time()
    num_boxes = regions.size(0)
    regions = regions.view(num_boxes, 2, 2)
    fence_regions = MultiPolygon([
        box(regions[i, 0, 0], regions[i, 0, 1], regions[i, 1, 0],
            regions[i, 1, 1]) for i in range(num_boxes)
    ])
    # print("fence region:", time.time()-tt)

    merged_macros = macros.union(fence_regions)

    slices = []
    for p in merged_macros:
        boundary_x, _ = p.boundary.xy
        boundary_x = boundary_x.tolist()
        if len(boundary_x) == 5:
            slices.append(p.bounds)
        else:
            xs = sorted(list(set(boundary_x)))
            for i, x_l in enumerate(xs[:-1]):
                x_h = xs[i + 1]
                cvx_hull = box(x_l, yl, x_h, yh)
                intersect = p.intersection(cvx_hull)
                if isinstance(intersect, Polygon):
                    slices.append(intersect.bounds)
                elif isinstance(intersect, (GeometryCollection, MultiPolygon)):
                    slices.extend([
                        j.bounds for j in intersect if (isinstance(j, Polygon))
                    ])

    # tt = time.time()
    if merge:
        raw_bbox_list = sorted(slices, key=lambda x: (x[1], x[0]))
        cur_bbox = None
        bbox_list = []
        for i, p in enumerate(raw_bbox_list):
            minx, miny, maxx, maxy = p
            if cur_bbox is None:
                cur_bbox = [minx, miny, maxx, maxy]
            elif cur_bbox[1] == miny and cur_bbox[3] == maxy:
                cur_bbox[2:] = p[2:]
            else:
                bbox_list.append(cur_bbox)
                cur_bbox = [minx, miny, maxx, maxy]
        else:
            bbox_list.append(cur_bbox)
    else:
        bbox_list = slices
    # print("merge:", time.time()-tt)

    bbox_list = torch.tensor(bbox_list).float()
    pos_x = bbox_list[:, 0]
    pos_y = bbox_list[:, 1]
    node_size_x = bbox_list[:, 2] - bbox_list[:, 0]
    node_size_y = bbox_list[:, 3] - bbox_list[:, 1]

    if plot:
        from descartes.patch import PolygonPatch
        from matplotlib import pyplot as plt

        from figures import BLUE, SIZE, color_isvalid, plot_coords, set_limits

        res = []
        for bbox in bbox_list:
            res.append(box(*bbox))
        res = MultiPolygon(res)
        fig = plt.figure(1, figsize=SIZE, dpi=90)
        ax = fig.add_subplot(111)
        for polygon in res:
            # plot_coords(ax, polygon.exterior)
            patch = PolygonPatch(
                polygon,
                facecolor=color_isvalid(merged_macros),
                edgecolor=color_isvalid(merged_macros, valid=BLUE),
                alpha=0.5,
                zorder=2,
            )
            ax.add_patch(patch)

        set_limits(ax, -1, 20, -1, 20)
        # ax = fig.add_subplot(122)
        # patch = PolygonPatch(reverse, facecolor=color_isvalid(reverse), edgecolor=color_isvalid(reverse, valid=BLUE), alpha=0.5, zorder=2)
        # ax.add_patch(patch)
        # set_limits(ax, -1, 20, -1, 20)
        plt.savefig("macro.png")

    return pos_x, pos_y, node_size_x, node_size_y
Example #13
0
def DiagnoseAndFixMultiPolygon(attributeName,boundariesList, printDiagnostic = False):
    print "---"
    print "*** %s is invalid. Running diagnostic... ***"%(attributeName)
    # Note: we have already tested each polygon, so the problem is when we add them to a multipolygon
    # the likely cause is overlapping polygons, so we will automatically union those polygons 
    #printDiagnostic = True
    goodPolygons = []
    badPolygons = []
    useSlowerCode = False
    numPolygons = len(boundariesList)
    if useSlowerCode:
        for i in range(0,numPolygons) :
            if i % 20 == 0:
                print "processing polygon", i
            thisPolygon = boundariesList[i]
            shapelyMultiPoly = MultiPolygon(goodPolygons + [thisPolygon])
            if shapelyMultiPoly.is_valid:
                goodPolygons.append(thisPolygon)
            else:
                print "*** problem when adding polygon %d ***"%(i)
                print thisPolygon
                badPolygons.append(thisPolygon)
    else:
        ''' it can be very slow trying these one at a time, 
        so lets try jumping when we can and dropping back to one at a time when we have a problem
        '''
        numPolygons = len(boundariesList)
        if printDiagnostic: print "numPolygons",numPolygons
        
        i = 0
        lineReported = 0
        while True:
            if i >= numPolygons:
                if printDiagnostic: print "reached end of list"
                break # end of list
            
            lineToReport = (i/100)*100
            if lineToReport != lineReported:
                lineReported = lineToReport
                if i > 0:
                    print "processing polygon %d of %d"%(i,numPolygons)
                
            continueToTop = False
            for numToJump in [100,50,20,10]:
                if continueToTop: continue
                if i + numToJump > numPolygons:
                    numToJump = numPolygons - i;
                    if printDiagnostic: print "adjusting numToJump to",numToJump
                #try the jump
                if printDiagnostic: print "trying jump"
                polygonsToAdd = boundariesList[i:i+numToJump]
                shapelyMultiPoly = MultiPolygon(goodPolygons + polygonsToAdd)
                if shapelyMultiPoly.is_valid:
                    goodPolygons = goodPolygons + polygonsToAdd
                    i = i + numToJump
                    if printDiagnostic: print "jumped to %i"%(i)
                    continueToTop = True
                
            if continueToTop: continue
                            
            #resort to one at a time
            if printDiagnostic: print "resorting to one at a time. i = %d"%(i)
            for j in range(0,numToJump):              
                thisPolygon = boundariesList[i]
                shapelyMultiPoly = MultiPolygon(goodPolygons + [thisPolygon])
                if shapelyMultiPoly.is_valid:
                    goodPolygons.append(thisPolygon)
                else:
                    if True: print "polygon %d is bad"%(i)
                    badPolygons.append(thisPolygon)
                i = i+1

    # now automatically generate a "fixed" multipolygon by unioning the bad polygons
    shapelyMultiPoly = MultiPolygon(goodPolygons)
    if len(badPolygons) > 0: 
        print "---"
        print "*** Handling bad polygons ***"
        for poly in badPolygons:
            print "Bad polygon:", poly
            islandPoly = MultiPolygon([poly])
            if islandPoly.is_valid:
                print "Fixed: The bad polygon was a valid polygon, it has been unioned to the whole."
                shapelyMultiPoly = shapelyMultiPoly.union(islandPoly)
            else:
                # try to fix this polygon
                '''
                Outer boundary problems:
                Sometimes with clipping of shoreline, there are cases of the outer boundary of the
                shoreline crossing itself.  There is not really anything we can do about such cases.
                '''
                # check to see if the outer boundary is valid
                outerBoundary,innerBoundaries = poly
                
                islandPoly = MultiPolygon([(outerBoundary,[])])
                if not islandPoly.is_valid:
                    print "Outer boundary is not valid."
                    print "Unable to fix this polygon."
                    if attributeName == "MAPLAND":
                        print "This is a minor error, it just means the oil contours will not be clipped to this part of the land." 
                    else:
                        print "This is a serious error.  Part of the %s area will be missing."%(attributeName)
                    print "---"
                    continue # we will omit this polygon
                
                print "The outer boundary of the polygon is valid."
                
                
                ##############################
                '''
                Hole problems:
                There are two kinds of problems that can occur with GNOME Analyst contours.
                
                Sometimes the holes stick slightly out of the outerboundary.
                In such a case we will rely on the fact that the holes were just areas to be removed.
                
                Sometimes there seem to be holes within holes. 
                I'm not sure why GNOME Analyst is doing that, but we will assume that 
                the holes were just areas to be removed.
                '''
                numHoles = len(innerBoundaries)
                if numHoles > 0: print "Examing the %d holes..."%(numHoles)
                assert numHoles > 0 # the only way to get to this part of the code is for a hole to be causing the problem
                numOmittedHoles = 0
                for innerBoundary in innerBoundaries:
                    hole = Polygon(innerBoundary)
                    if not hole.is_valid:
                        numOmittedHoles = numOmittedHoles + 1
                        print "Hole is not valid:", hole 
                        print "Omitting this hole. This is a minor error."
                    else:
                        # subtract this hole from the islandPolygon
                        islandPoly = islandPoly.difference(hole)
                    
                if numOmittedHoles > 0: 
                    print "Partially fixed: %d invalid holes were not subtracted from this polygon, but this polygon has been unioned to the whole."%(numOmittedHoles)
                elif numHoles > 0: 
                    print "Fixed: all holes successfully subtracted from this polygon and the polygon unioned to the whole."
                else : 
                    print "Fixed: polygon unioned to the whole."
                
                shapelyMultiPoly = shapelyMultiPoly.union(islandPoly)
                
            print "---"
        
    return shapelyMultiPoly
Example #14
0
    # Find convex hull for points; find mean of x and y values
    xy = np.vstack([concat['x'].values,concat['y']]).T
    vertices = xy
    if len(xy) > 2:
        hull = ConvexHull(xy)
        vertices = hull.points[hull.simplices]
        vertices = np.vstack([vertices[:,0],vertices[:,1]])
        vertices = DataFrame(vertices).drop_duplicates().values
    x = np.mean(vertices[:,0])
    new['x'] = x
    y = np.mean(vertices[:,1])
    new['y'] = y

    new_poly = MultiPolygon()
    for poly in concat['region']:
        new_poly = new_poly.union(poly)
    new['region'] = [new_poly]

    new['v_area'] = new['region'][0].area
    new['v_larea'] = np.log(new['v_area'])

    new = DataFrame(new)
    stops = stops.append(new)

print "OK"

print 'calculating connectedness...'

print '    building representation of subway system...'
# # 2.3 calculate connectedness
# # 2.3.1 generate unique routes
Example #15
0
    def custom_render(self, level_render_data, access_permissions,
                      full_levels):
        if full_levels:
            levels = get_full_levels(level_render_data)
        else:
            levels = get_main_levels(level_render_data)

        buildings = None
        areas = None

        main_building_block = None
        main_building_block_diff = None

        current_upper_bound = None
        for geoms in levels:
            # hide indoor and outdoor rooms if their access restriction was not unlocked
            restricted_spaces_indoors = unary_union(
                tuple(area.geom for access_restriction, area in
                      geoms.restricted_spaces_indoors.items()
                      if access_restriction not in access_permissions))
            restricted_spaces_outdoors = unary_union(
                tuple(area.geom for access_restriction, area in
                      geoms.restricted_spaces_outdoors.items()
                      if access_restriction not in access_permissions))
            restricted_spaces = unary_union(
                (restricted_spaces_indoors,
                 restricted_spaces_outdoors))  # noqa

            # crop altitudeareas
            for altitudearea in geoms.altitudeareas:
                altitudearea.geometry = altitudearea.geometry.geom.difference(
                    restricted_spaces)
                altitudearea.geometry_prep = prepared.prep(
                    altitudearea.geometry)

            # crop heightareas
            new_heightareas = []
            for geometry, height in geoms.heightareas:
                geometry = geometry.geom.difference(restricted_spaces)
                geometry_prep = prepared.prep(geometry)
                new_heightareas.append((geometry, geometry_prep, height))
            geoms.heightareas = new_heightareas

            if geoms.on_top_of_id is None:
                buildings = geoms.buildings
                areas = MultiPolygon()
                current_upper_bound = geoms.upper_bound

                holes = geoms.holes.difference(restricted_spaces)
                buildings = buildings.difference(holes)
                areas = areas.union(
                    holes.buffer(0).buffer(0.01, join_style=JOIN_STYLE.mitre))

                main_building_block = OpenScadBlock('union()',
                                                    comment='Level %s' %
                                                    geoms.short_label)
                self.root.append(main_building_block)
                main_building_block_diff = OpenScadBlock('difference()')
                main_building_block.append(main_building_block_diff)
                main_building_block_inner = OpenScadBlock('union()')
                main_building_block_diff.append(main_building_block_inner)
                main_building_block_inner.append(
                    self._add_polygon(None, buildings.intersection(self.bbox),
                                      geoms.lower_bound, geoms.upper_bound))

            for altitudearea in sorted(geoms.altitudeareas,
                                       key=attrgetter('altitude')):
                if not altitudearea.geometry.intersects(self.bbox):
                    continue

                if altitudearea.altitude2 is not None:
                    name = 'Altitudearea %s-%s' % (
                        altitudearea.altitude / 1000,
                        altitudearea.altitude2 / 1000)
                else:
                    name = 'Altitudearea %s' % (altitudearea.altitude / 1000)

                # why all this buffering?
                # buffer(0) ensures a valid geometry, this is sadly needed sometimes
                # the rest of the buffering is meant to make polygons overlap a little so no glitches appear
                # the intersections below will ensure that they they only overlap with each other and don't eat walls
                geometry = altitudearea.geometry.buffer(0)
                inside_geometry = geometry.intersection(buildings).buffer(
                    0).buffer(0.01, join_style=JOIN_STYLE.mitre)
                outside_geometry = geometry.difference(buildings).buffer(
                    0).buffer(0.01, join_style=JOIN_STYLE.mitre)
                geometry_buffered = geometry.buffer(
                    0.01, join_style=JOIN_STYLE.mitre)
                if geoms.on_top_of_id is None:
                    areas = areas.union(geometry)
                    buildings = buildings.difference(geometry).buffer(0)
                    inside_geometry = inside_geometry.intersection(
                        areas).buffer(0)
                    outside_geometry = outside_geometry.intersection(
                        areas).buffer(0)
                    geometry_buffered = geometry_buffered.intersection(
                        areas).buffer(0)
                outside_geometry = outside_geometry.intersection(self.bbox)

                if not inside_geometry.is_empty:
                    if altitudearea.altitude2 is not None:
                        min_slope_altitude = min(altitudearea.altitude,
                                                 altitudearea.altitude2)
                        max_slope_altitude = max(altitudearea.altitude,
                                                 altitudearea.altitude2)
                        bounds = inside_geometry.bounds

                        # cut in
                        polygon = self._add_polygon(None, inside_geometry,
                                                    min_slope_altitude - 10,
                                                    current_upper_bound + 1000)

                        slope = self._add_slope(bounds,
                                                altitudearea.altitude,
                                                altitudearea.altitude2,
                                                altitudearea.point1,
                                                altitudearea.point2,
                                                bottom=True)
                        main_building_block_diff.append(
                            OpenScadBlock('difference()',
                                          children=[polygon, slope],
                                          comment=name + ' inside cut'))

                        # actual thingy
                        if max_slope_altitude > current_upper_bound and inside_geometry.intersects(
                                self.bbox):
                            polygon = self._add_polygon(
                                None, inside_geometry.intersection(self.bbox),
                                current_upper_bound - 10,
                                max_slope_altitude + 10)
                            slope = self._add_slope(bounds,
                                                    altitudearea.altitude,
                                                    altitudearea.altitude2,
                                                    altitudearea.point1,
                                                    altitudearea.point2,
                                                    bottom=False)
                            main_building_block.append(
                                OpenScadBlock('difference()',
                                              children=[polygon, slope],
                                              comment=name + 'outside'))
                    else:
                        if altitudearea.altitude < current_upper_bound:
                            main_building_block_diff.append(
                                self._add_polygon(name + ' inside cut',
                                                  inside_geometry,
                                                  altitudearea.altitude,
                                                  current_upper_bound + 1000))
                        else:
                            main_building_block.append(
                                self._add_polygon(
                                    name + ' inside',
                                    inside_geometry.intersection(self.bbox),
                                    min(altitudearea.altitude - 700,
                                        current_upper_bound - 10),
                                    altitudearea.altitude))

                if not outside_geometry.is_empty:
                    if altitudearea.altitude2 is not None:
                        min_slope_altitude = min(altitudearea.altitude,
                                                 altitudearea.altitude2)
                        max_slope_altitude = max(altitudearea.altitude,
                                                 altitudearea.altitude2)
                        bounds = outside_geometry.bounds

                        polygon = self._add_polygon(None, outside_geometry,
                                                    min_slope_altitude - 710,
                                                    max_slope_altitude + 10)
                        slope1 = self._add_slope(bounds,
                                                 altitudearea.altitude,
                                                 altitudearea.altitude2,
                                                 altitudearea.point1,
                                                 altitudearea.point2,
                                                 bottom=False)
                        slope2 = self._add_slope(bounds,
                                                 altitudearea.altitude - 700,
                                                 altitudearea.altitude2 - 700,
                                                 altitudearea.point1,
                                                 altitudearea.point2,
                                                 bottom=True)
                        union = OpenScadBlock('union()',
                                              children=[slope1, slope2],
                                              comment=name + 'outside')
                        main_building_block.append(
                            OpenScadBlock('difference()',
                                          children=[polygon, union],
                                          comment=name + 'outside'))
                    else:
                        if geoms.on_top_of_id is None:
                            lower = geoms.lower_bound
                        else:
                            lower = altitudearea.altitude - 700
                            if lower == current_upper_bound:
                                lower -= 10
                        main_building_block.append(
                            self._add_polygon(name + ' outside',
                                              outside_geometry, lower,
                                              altitudearea.altitude))

                # obstacles
                if altitudearea.altitude2 is not None:
                    obstacles_diff_block = OpenScadBlock('difference()',
                                                         comment=name +
                                                         ' obstacles')
                    had_obstacles = False

                    obstacles_block = OpenScadBlock('union()')
                    obstacles_diff_block.append(obstacles_block)

                    min_slope_altitude = min(altitudearea.altitude,
                                             altitudearea.altitude2)
                    max_slope_altitude = max(altitudearea.altitude,
                                             altitudearea.altitude2)
                    bounds = geometry.bounds

                    for height, obstacles in altitudearea.obstacles.items():
                        height_diff = OpenScadBlock('difference()')
                        had_height_obstacles = None

                        height_union = OpenScadBlock('union()')
                        height_diff.append(height_union)

                        for obstacle in obstacles:
                            if not obstacle.geom.intersects(self.bbox):
                                continue
                            obstacle = obstacle.geom.buffer(0).buffer(
                                0.01, join_style=JOIN_STYLE.mitre)
                            if self.min_width:
                                obstacle = obstacle.union(
                                    self._satisfy_min_width(obstacle)).buffer(
                                        0)
                            obstacle = obstacle.intersection(geometry_buffered)
                            if not obstacle.is_empty:
                                had_height_obstacles = True
                                had_obstacles = True
                            height_union.append(
                                self._add_polygon(
                                    None, obstacle.intersection(self.bbox),
                                    min_slope_altitude - 20,
                                    max_slope_altitude + height + 10))

                        if had_height_obstacles:
                            obstacles_block.append(height_diff)
                            height_diff.append(
                                self._add_slope(bounds,
                                                altitudearea.altitude + height,
                                                altitudearea.altitude2 +
                                                height,
                                                altitudearea.point1,
                                                altitudearea.point2,
                                                bottom=False))

                    if had_obstacles:
                        main_building_block.append(obstacles_diff_block)
                        obstacles_diff_block.append(
                            self._add_slope(bounds,
                                            altitudearea.altitude - 10,
                                            altitudearea.altitude2 - 10,
                                            altitudearea.point1,
                                            altitudearea.point2,
                                            bottom=True))
                else:
                    obstacles_block = OpenScadBlock('union()',
                                                    comment=name +
                                                    ' obstacles')
                    had_obstacles = False
                    for height, obstacles in altitudearea.obstacles.items():
                        for obstacle in obstacles:
                            if not obstacle.geom.intersects(self.bbox):
                                continue
                            obstacle = obstacle.geom.buffer(0).buffer(
                                0.01, join_style=JOIN_STYLE.mitre)
                            if self.min_width:
                                obstacle = obstacle.union(
                                    self._satisfy_min_width(obstacle)).buffer(
                                        0)
                            obstacle = obstacle.intersection(
                                geometry_buffered).intersection(self.bbox)
                            if not obstacle.is_empty:
                                had_obstacles = True
                            obstacles_block.append(
                                self._add_polygon(
                                    None, obstacle, altitudearea.altitude - 10,
                                    altitudearea.altitude + height))

                    if had_obstacles:
                        main_building_block.append(obstacles_block)

            if self.min_width and geoms.on_top_of_id is None:
                main_building_block_inner.append(
                    self._add_polygon(
                        'min width',
                        self._satisfy_min_width(buildings).intersection(
                            self.bbox).buffer(0), geoms.lower_bound,
                        geoms.upper_bound))