def generate_offset_track(self, mid_track, thickness):
        poly_line = LinearRing(mid_track)
        poly_line_offset_left = poly_line.parallel_offset(thickness,
                                                          side="left",
                                                          resolution=16,
                                                          join_style=2,
                                                          mitre_limit=2)
        poly_line_offset_right = poly_line.parallel_offset(thickness,
                                                           side="right",
                                                           resolution=16,
                                                           join_style=2,
                                                           mitre_limit=2)

        offset_left_track = []
        for coord in poly_line_offset_left.coords:
            p = [coord[0], coord[1]]
            offset_left_track.append(p)
        offset_left_track.append(offset_left_track[0])

        offset_right_track = []
        for coord in poly_line_offset_right.coords:
            p = [coord[0], coord[1]]
            offset_right_track.append(p)
        offset_right_track.append(offset_right_track[0])

        return offset_left_track, offset_right_track
Exemplo n.º 2
0
    def test_stitch(self):
        # The following LinearRing wanders in/out of the map domain
        # but importantly the "vertical" lines at 0'E and 360'E are both
        # chopped by the map boundary. This results in their ends being
        # *very* close to each other and confusion over which occurs
        # first when navigating around the boundary.
        # Check that these ends are stitched together to avoid the
        # boundary ordering ambiguity.
        # NB. This kind of polygon often occurs with MPL's contouring.
        coords = [(0.0, -70.70499926182919), (0.0, -71.25), (0.0, -72.5),
                  (0.0, -73.49076371657017), (360.0, -73.49076371657017),
                  (360.0, -72.5), (360.0, -71.25), (360.0, -70.70499926182919),
                  (350, -73), (10, -73)]
        src_proj = ccrs.PlateCarree()
        target_proj = ccrs.Stereographic(80)

        linear_ring = LinearRing(coords)
        rings, mlinestr = target_proj.project_geometry(linear_ring, src_proj)
        self.assertEqual(len(mlinestr), 1)
        self.assertEqual(len(rings), 0)

        # Check the stitch works in either direction.
        linear_ring = LinearRing(coords[::-1])
        rings, mlinestr = target_proj.project_geometry(linear_ring, src_proj)
        self.assertEqual(len(mlinestr), 1)
        self.assertEqual(len(rings), 0)
Exemplo n.º 3
0
def boundary_of_drivemap(drivemap, footprint, height=1.0, edge_width=0.25):
    """
    Construct an object boundary using the manually recorded corner points.
    Do this by finding all the points in the drivemap along the footprint.
    Use the bottom 'height' meters of the drivemap (not trees).
    Resulting pointcloud has the same SRS and offset as the input.

    Arguments:
        drivemap   : pcl.PointCloud
        footprint  : pcl.PointCloud
        height     : Cut-off height, points more than this value above the
                     lowest point of the drivemap are considered trees,
                     and dropped. default 1 m.
        edge_width : Points belong to the boundary when they are within
                     this distance from the footprint. default 0.25

    Returns:
        boundary   : pcl.PointCloud
    """

    # construct basemap as the bottom 'height' meters of the drivemap

    drivemap_array = np.asarray(drivemap)
    bb = BoundingBox(points=drivemap_array)
    basemap = extract_mask(drivemap, drivemap_array[:, 2] < bb.min[2] + height)

    # Cut band between +- edge_width around the footprint
    edge = LinearRing(np.asarray(footprint)).buffer(edge_width)
    boundary = extract_mask(basemap,
                            [edge.contains(asPoint(pnt)) for pnt in basemap])

    utils.force_srs(boundary, same_as=basemap)

    return boundary
Exemplo n.º 4
0
            def intersections(a, b):
                ea = LinearRing(a)
                eb = LinearRing(b)
                mp = ea.intersection(eb)

                x = [p.x for p in mp]
                y = [p.y for p in mp]
                return x, y
Exemplo n.º 5
0
def test_linearring_from_coordinate_sequence():
    expected_coords = [(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (0.0, 0.0)]

    ring = LinearRing(((0.0, 0.0), (0.0, 1.0), (1.0, 1.0)))
    assert ring.coords[:] == expected_coords

    ring = LinearRing([(0.0, 0.0), (0.0, 1.0), (1.0, 1.0)])
    assert ring.coords[:] == expected_coords
Exemplo n.º 6
0
def test_linearring_from_empty():
    ring = LinearRing()
    assert ring.is_empty
    assert ring.coords[:] == []

    ring = LinearRing([])
    assert ring.is_empty
    assert ring.coords[:] == []
Exemplo n.º 7
0
 def test_linearring_from_short_closed_linestring(self):
     # Create linearring from linestring where the coordinate sequence is
     # too short but appears to be closed (first and last coordinates
     # are the same)
     coords = [(0.0, 0.0), (0.0, 0.0), (0.0, 0.0)]
     line = LineString(coords)
     ring1 = LinearRing(coords)
     ring2 = LinearRing(line)
     assert ring1.coords[:] == ring2.coords[:]
Exemplo n.º 8
0
def test_linearring_from_empty():
    ring = LinearRing()
    assert ring.is_empty
    assert isinstance(ring.coords, CoordinateSequence)
    assert ring.coords[:] == []

    ring = LinearRing([])
    assert ring.is_empty
    assert isinstance(ring.coords, CoordinateSequence)
    assert ring.coords[:] == []
def ellipse_intersect(a, b, ret_points=False):
    ea = LinearRing(a)
    eb = LinearRing(b)
    mp = ea.intersection(eb)

    if ret_points:
        x = [p.x for p in mp]
        y = [p.y for p in mp]
        return x, y
    return bool(mp)
Exemplo n.º 10
0
    def test_linearring_mutate(self):
        coords = ((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0))
        ring = LinearRing(coords)

        # Coordinate modification
        ring.coords = ((0.0, 0.0), (0.0, 2.0), (2.0, 2.0), (2.0, 0.0))
        self.assertEqual(
            ring.__geo_interface__,
            {'type': 'LinearRing',
             'coordinates': ((0.0, 0.0), (0.0, 2.0), (2.0, 2.0), (2.0, 0.0),
                             (0.0, 0.0))})
Exemplo n.º 11
0
    def test_linearring(self):

        # Initialization
        # Linear rings won't usually be created by users, but by polygons
        coords = ((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0))
        ring = LinearRing(coords)
        self.assertEqual(len(ring.coords), 5)
        self.assertEqual(ring.coords[0], ring.coords[4])
        self.assertEqual(ring.coords[0], ring.coords[-1])
        self.assertTrue(ring.is_ring)

        # Ring from sequence of Points
        self.assertEqual(LinearRing((map(Point, coords))), ring)
Exemplo n.º 12
0
def test_linearring_from_too_short_linestring():
    # Creation of LinearRing request at least 3 coordinates (unclosed) or
    # 4 coordinates (closed)
    coords = [(0.0, 0.0), (1.0, 1.0)]
    line = LineString(coords)
    with pytest.raises(ValueError, match="at least 3 coordinate tuple"):
        LinearRing(line)
Exemplo n.º 13
0
    def test_cuts(self):
        # Check that fragments do not start or end with one of the
        # original ... ?
        linear_ring = LinearRing([(-10, 30), (10, 60), (10, 50)])
        projection = ccrs.Robinson(170.5)
        rings, multi_line_string = projection.project_geometry(linear_ring)

        # The original ring should have been split into multiple pieces.
        self.assertGreater(len(multi_line_string), 1)
        self.assertFalse(rings)

        def assert_intersection_with_boundary(segment_coords):
            # Double the length of the segment.
            start = segment_coords[0]
            end = segment_coords[1]
            end = [end[i] + 2 * (end[i] - start[i]) for i in (0, 1)]
            extended_segment = sgeom.LineString([start, end])
            # And see if it crosses the boundary.
            intersection = extended_segment.intersection(projection.boundary)
            self.assertFalse(intersection.is_empty,
                             'Bad topology near boundary')

        # Each line resulting from the split should start and end with a
        # segment that crosses the boundary when extended to double length.
        # (This is important when considering polygon rings which need to be
        # attached to the boundary.)
        for line_string in multi_line_string:
            coords = list(line_string.coords)
            self.assertGreaterEqual(len(coords), 2)
            assert_intersection_with_boundary(coords[1::-1])
            assert_intersection_with_boundary(coords[-2:])
Exemplo n.º 14
0
def test_polygon_from_linearring():
    coords = [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)]
    ring = LinearRing(coords)

    polygon = Polygon(ring)
    assert polygon.exterior.coords[:] == coords
    assert len(polygon.interiors) == 0
Exemplo n.º 15
0
def TMApoly(Fwaypt = None, fname_STA = None):
        MeterPntList = []
        if Fwaypt != None:
            with open(Fwaypt,'r') as csvfile:
                line = csv.reader(csvfile)
                for field in line:
                    MeterPntList.append([float(field[1]),float(field[0])])
#        else:
#            All_Meter_Name = np.array([])
#            for name in fname_STA:
#                a = np.genfromtxt(os.getcwd()+'\STA_DATA\\'+name, usecols = (6,7,8),dtype=[('RWY','S10'),('FAF','S10'),('MFX','S10')],delimiter=",")
#                All_Meter_Name = np.append(All_Meter_Name,a['MFX'])
#            All_Meter_Name = np.unique(All_Meter_Name)
#            with open('WayPoint1.csv','wb') as wcsvfile:
#                wri = csv.writer(wcsvfile)
#                for MFX in All_Meter_Name:
#                    Coords = FixCoords(MFX)
#                    if len(Coords) != 0:
#                        MeterPntList.append(Coords)
#                        wri.writerow(Coords+[MFX])
#                    else:
#                        pass
        
        mlat = sum(x[0] for x in MeterPntList) / len(MeterPntList)
        mlng = sum(x[1] for x in MeterPntList) / len(MeterPntList)

        def algo(x):
            return (math.atan2(x[0] - mlat, x[1] - mlng) + 2 * math.pi) % (2*math.pi)
            
        MeterPntList.sort(key=algo)
        TMA_Ring = LinearRing(MeterPntList)
        TMA_Poly = Polygon(MeterPntList)
        return TMA_Poly, TMA_Ring, MeterPntList
Exemplo n.º 16
0
def get_intersecting_boundaries_for_polygon(location: geodetic_polygon, boundary_table, engine, return_intersection_area=False, proximity_search = False, proximity_buffer = 0 ):
    """
    Executes an intersection query for a polygon.

    :param location: location object
    :type location: :py:class:Geodetic2D
    :param boundary_table: The name of the service boundary table.
    :type boundary_table: `str`
    :param engine: SQLAlchemy database engine.
    :type engine: :py:class:`sqlalchemy.engine.Engine`
    :param return_intersection_area: Flag which triggers an area calculation on the Intersecting polygons.
    :type return_intersection_area: `bool`
    :return: A list of dictionaries containing the contents of returned rows.
    """
    # Pull out just the number from the SRID
    trimmed_srid = int(location.spatial_ref.split('::')[1])

    points = location.vertices
    ring = LinearRing(points)
    shapely_polygon = Polygon(ring)

    # load up a new Shapely Polygon from the WKT and convert it to a GeoAlchemy2 WKBElement
    # that we can use to query.
    poly = loads(shapely_polygon.wkt)
    wkb_poly = location.to_wkbelement(project_to=trimmed_srid)

    if proximity_search == True:
        return get_intersecting_boundaries_with_buffer(points[0][0], points[0][1], engine, boundary_table, wkb_poly,
                                                proximity_buffer, return_intersection_area)
    else:
        return _get_intersecting_boundaries_for_geom(engine, boundary_table, wkb_poly, return_intersection_area)
Exemplo n.º 17
0
    def to_poly(self):
        """Convert to a polygon.

        Returns:
            (shapely.geometry.polygon.LinearRing): Outline of the hexagon.

        """
        centre = self.to_geographic()

        # the corners of a hexagon are at 60 degree increments. Start halfway
        # through an increment because we have flat topped ones
        DEG_TO_RAD = math.pi / 180
        angles = [
            30 * DEG_TO_RAD,
            90 * DEG_TO_RAD,
            150 * DEG_TO_RAD,
            210 * DEG_TO_RAD,
            270 * DEG_TO_RAD,
            330 * DEG_TO_RAD
        ]

        return LinearRing(
            [
                (
                    centre[0] + self.D * math.sin(t),
                    centre[1] + self.D * math.cos(t)
                )
                for t in angles
            ]
        )
Exemplo n.º 18
0
 def is_ccw(self, region):
     coords = self.get_region_coords(region)
     ring = LinearRing(coords)
     if ring.is_ccw:
         return True
     else:
         return False
Exemplo n.º 19
0
def add_box(ax, x0, x1, y0, y1, **kwargs):
    """
    Add a polygon/box to any cartopy projection.

    Parameters
    ----------
    ax : axes instance (should be from make_cartopy command)
    x0: float; western longitude bound of box.
    x1: float; eastern longitude bound of box.
    y0: float; southern latitude bound of box.
    y1: float; northern latitude bound of box.
    **kwargs: optional keywords
        Will modify the color, etc. of the bounding box.

    Returns
    -------
    None

    Examples
    --------
    import esm_analysis as et
    fig, ax = et.vis.make_cartopy()
    et.visualization.add_box(ax, [-150, -110, 30, 50], edgecolor='k',
                             facecolor='#D3D3D3', linewidth=2, alpha=0.5)
    """
    lons = [x0, x0, x1, x1]
    lats = [y0, y1, y1, y0]
    ring = LinearRing(list(zip(lons, lats)))
    ax.add_geometries([ring], ccrs.PlateCarree(), **kwargs)
Exemplo n.º 20
0
def test_linearring_from_numpy():
    # Construct from a numpy array
    np = pytest.importorskip("numpy")
    coords = [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)]

    ring = LinearRing(np.array(coords))
    assert ring.coords[:] == [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)]
Exemplo n.º 21
0
def highres_Orthographic(projection, r=R_Mars_km * 1e3):
    """
    Increases the resolution of the circular outline in cartopy.crs.Orthographic projection.

    Parameters
    ----------
    projection : obj
        A cartopy.crs.Orthographic() projection.
    r : float
        The radius of the globe in meters (e.g., for Mars this is the radius of Mars in meters).

    Returns
    -------
    None. Changes the resolution of an existing projection.
    """

    # re-implement the cartopy code to figure out the new boundary shape
    a = np.float(projection.globe.semimajor_axis or r)
    b = np.float(projection.globe.semiminor_axis or a)
    t = np.linspace(0, 2 * np.pi, 3601)
    coords = np.vstack([a * 0.99999 * np.cos(t),
                        b * 0.99999 * np.sin(t)])[:, ::-1]

    # update the projection boundary
    projection._boundary = LinearRing(coords.T)
Exemplo n.º 22
0
def get_intersecting_list_service_for_polygon(location: geodetic_polygon, boundary_table, engine, return_intersection_area=False, proximity_search = False, proximity_buffer = 0 ):
    """
    Executes an intersection query for a polygon.

    :param location: location object
    :type location: :py:class:Geodetic2D
    :param boundary_table: The name of the service boundary table.
    :type boundary_table: `str`
    :param engine: SQLAlchemy database engine.
    :type engine: :py:class:`sqlalchemy.engine.Engine`
    :param return_intersection_area: Flag which triggers an area calculation on the Intersecting polygons.
    :type return_intersection_area: `bool`
    :return: A list of dictionaries containing the contents of returned rows.
    """
    # Pull out just the number from the SRID
    trimmed_srid = int(location.spatial_ref.split('::')[1])
    p = []
    points = location.vertices
    for point in points:
        long, lat = gc_geom.reproject_point(point[0], point[1], trimmed_srid, 4326)
        p.append([long, lat])

    ring = LinearRing(p)
    wkb_ring = location.to_wkbelement(project_to=trimmed_srid)

    return (_get_intersecting_list_service_for_geom(engine, i, wkb_ring, return_intersection_area) for i in
            boundary_table)
Exemplo n.º 23
0
    def test_out_of_bounds(self):
        # Check that a ring that is completely out of the map boundary
        # produces an empty result.
        # XXX Check efficiency?
        projection = ccrs.TransverseMercator(central_longitude=0)

        rings = [
            # All valid
            ([(86, 1), (86, -1), (88, -1), (88, 1)], -1),
            # One NaN
            ([(86, 1), (86, -1), (130, -1), (88, 1)], 1),
            # A NaN segment
            ([(86, 1), (86, -1), (130, -1), (130, 1)], 1),
            # All NaN
            ([(120, 1), (120, -1), (130, -1), (130, 1)], 0),
        ]

        # Try all four combinations of valid/NaN vs valid/NaN.
        for coords, expected_n_lines in rings:
            linear_ring = LinearRing(coords)
            rings, mlinestr = projection.project_geometry(linear_ring)
            if expected_n_lines == -1:
                self.assertTrue(rings)
                self.assertFalse(mlinestr)
            else:
                self.assertEqual(len(mlinestr), expected_n_lines)
                if expected_n_lines == 0:
                    self.assertTrue(mlinestr.is_empty)
Exemplo n.º 24
0
def highres_NearsidePerspective(projection, altitude, r=R_Mars_km * 1e3):
    """
    Increases the resolution of the circular outline in cartopy.crs.NearsidePerspective projection.

    Parameters
    ----------
    projection : obj
        A cartopy.crs.NearsidePerspective() projection.
    altitude : int, float
        Apoapse altitude in meters.
    r : float
        The radius of the globe in meters (e.g., for Mars this is the radius of Mars in meters).

    Returns
    -------
    None. Changes the resolution of an existing projection.
    """

    # re-implement the cartopy code to figure out the new boundary shape
    a = np.float(projection.globe.semimajor_axis or r)
    h = np.float(altitude)
    max_x = a * np.sqrt(h / (2 * a + h))
    t = np.linspace(0, 2 * np.pi, 3601)
    coords = np.vstack([max_x * np.cos(t), max_x * np.sin(t)])[:, ::-1]

    # update the projection boundary
    projection._boundary = LinearRing(coords.T)
 def _xywh_to_ring(self, x, y, width, height):
     points = [(x - (width / 2.0), y - (height / 2.0)),
               (x - (width / 2.0), y + (height / 2.0)),
               (x + (width / 2.0), y + (height / 2.0)),
               (x + (width / 2.0), y - (height / 2.0)),
               (x - (width / 2.0), y - (height / 2.0))]
     return Polygon(LinearRing(points))
Exemplo n.º 26
0
 def get_ring(coords):
     '''tuple in format: west_lon, east_lon, south_lat, north_lat '''
     west_lon, east_lon, south_lat, north_lat = coords
     lons_sq = [west_lon, west_lon, east_lon, east_lon]
     lats_sq = [north_lat, south_lat, south_lat, north_lat]
     ring = [LinearRing(list(zip(lons_sq, lats_sq)))]
     return ring
Exemplo n.º 27
0
def test_linearring_from_unclosed_linestring():
    coords = [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)]
    line = LineString(coords[:-1])  # Pass in unclosed line
    ring = LinearRing(line)
    assert len(ring.coords) == 4
    assert ring.coords[:] == coords
    assert ring.geom_type == 'LinearRing'
Exemplo n.º 28
0
 def test_linearring_from_too_short_linestring(self):
     # Creation of LinearRing request at least 3 coordinates (unclosed) or
     # 4 coordinates (closed)
     coords = [(0.0, 0.0), (0.0, 0.0)]
     line = LineString(coords)
     with self.assertRaises(ValueError):
         LinearRing(line)
Exemplo n.º 29
0
 def _get_geometry(self, element):
     # Point, LineString,
     # Polygon, LinearRing
     if element.tag == ('%sPoint' % self.ns):
         coords = self._get_coordinates(element)
         self._get_geometry_spec(element)
         return Point(coords[0])
     if element.tag == ('%sLineString' % self.ns):
         coords = self._get_coordinates(element)
         # issue seen with Garmin kml feeds with one coordinate linestrings
         if len(coords) < 2:
             logger.warn('LineStrings must have at least 2 coordinate tuples')
             return
         self._get_geometry_spec(element)
         return LineString(coords)
     if element.tag == ('%sPolygon' % self.ns):
         self._get_geometry_spec(element)
         outer_boundary = element.find('%souterBoundaryIs' % self.ns)
         ob = self._get_linear_ring(outer_boundary)
         inner_boundaries = element.findall('%sinnerBoundaryIs' % self.ns)
         ibs = []
         for inner_boundary in inner_boundaries:
             ibs.append(self._get_linear_ring(inner_boundary))
         return Polygon(ob, ibs)
     if element.tag == ('%sLinearRing' % self.ns):
         coords = self._get_coordinates(element)
         self._get_geometry_spec(element)
         return LinearRing(coords)
Exemplo n.º 30
0
    def intersections(self, a, b):
        """check if two polylines are intersected

        Args:
            a (polyline): polyline a
            b (polyline): polyline b

        Returns:
            boolean: true if polylines are intersected
        """
        try:
            ea = LinearRing(a)
            eb = LinearRing(b)
            return ea.intersection(eb)
        except:
            return False
Exemplo n.º 31
0
 def _get_geometry(self, element):
     # Point, LineString,
     # Polygon, LinearRing
     if element.tag == ('%sPoint' % self.ns):
         coords = self._get_coordinates(element)
         self._get_geometry_spec(element)
         return Point(coords[0])
     if element.tag == ('%sLineString' % self.ns):
         coords = self._get_coordinates(element)
         self._get_geometry_spec(element)
         return LineString(coords)
     if element.tag == ('%sPolygon' % self.ns):
         self._get_geometry_spec(element)
         outer_boundary = element.find('%souterBoundaryIs' % self.ns)
         ob = self._get_linear_ring(outer_boundary)
         inner_boundaries = element.findall('%sinnerBoundaryIs' % self.ns)
         ibs = [
             self._get_linear_ring(inner_boundary)
             for inner_boundary in inner_boundaries
         ]
         return Polygon(ob, ibs)
     if element.tag == ('%sLinearRing' % self.ns):
         coords = self._get_coordinates(element)
         self._get_geometry_spec(element)
         return LinearRing(coords)
Exemplo n.º 32
0
    def build_clusters(self, events, time_interval):
        events = list(events)
        event_count = len(events)

        cluster_count = 0

        if events:
            clustered_points, points = self.initialize_clusters(event_count, events)

            if len(points) < 3:
                return

            distances = pdist(points)
            results = fastcluster.linkage(distances)

            self.apply_results(results, clustered_points)

            self.cluster_builder.with_timestamp(time_interval.end).with_interval_seconds(time_interval.duration.seconds)

            for clustered_events in self.get_clustered_events(event_count, clustered_points):
                events_in_cluster = len(clustered_events)
                if events_in_cluster > 2:
                    points = np.ndarray([events_in_cluster, 2])
                    for index, strike in enumerate(clustered_events):
                        points[index][0] = strike.x
                        points[index][1] = strike.y
                    try:
                        hull = ConvexHull(points)
                    except QhullError:
                        self.logger.error("".join(str(points).splitlines()))
                        continue

                    shape_points = [
                        [
                            round(points[vertex, 0], self.coordinate_precision),
                            round(points[vertex, 1], self.coordinate_precision),
                        ]
                        for vertex in hull.vertices
                    ]

                    shape = LinearRing(shape_points)
                    shape = shape.buffer(self.buffer_size)
                    shape = shape.simplify(self.coordinate_accuracy, preserve_topology=False)
                    shape = shape.exterior

                    if shape is None:
                        continue

                    x_values = np.round(shape.coords.xy[0], self.coordinate_precision)
                    y_values = np.round(shape.coords.xy[1], self.coordinate_precision)
                    shape = LinearRing(zip(x_values, y_values))

                    cluster_count += 1

                    yield self.cluster_builder.with_strike_count(events_in_cluster).with_shape(shape).build()

            self.logger.debug(
                "build_clusters({} +{}): {} events -> {} clusters -> {} filtered".format(
                    time_interval.start, time_interval.duration, event_count, len(clustered_points), cluster_count
                )
            )
Exemplo n.º 33
0
def fig_intersects(fig1, fig2) -> float:
    lr1 = LinearRing(fig1)
    lr2 = LinearRing(fig2)
    return 1 if lr1.intersects(lr2) else 0
Exemplo n.º 34
0
    def test_polygon(self):

        # Initialization
        # Linear rings won't usually be created by users, but by polygons
        coords = ((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0))
        ring = LinearRing(coords)
        self.assertEqual(len(ring.coords), 5)
        self.assertEqual(ring.coords[0], ring.coords[4])
        self.assertEqual(ring.coords[0], ring.coords[-1])
        self.assertTrue(ring.is_ring)

        # Coordinate modification
        ring.coords = ((0.0, 0.0), (0.0, 2.0), (2.0, 2.0), (2.0, 0.0))
        self.assertEqual(
            ring.__geo_interface__,
            {'type': 'LinearRing',
             'coordinates': ((0.0, 0.0), (0.0, 2.0), (2.0, 2.0), (2.0, 0.0),
                             (0.0, 0.0))})

        # Test ring adapter
        coords = [[0.0, 0.0], [0.0, 1.0], [1.0, 1.0], [1.0, 0.0]]
        ra = asLinearRing(coords)
        self.assertTrue(ra.wkt.upper().startswith('LINEARRING'))
        self.assertEqual(dump_coords(ra),
                         [(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0),
                          (0.0, 0.0)])
        coords[3] = [2.0, -1.0]
        self.assertEqual(dump_coords(ra),
                         [(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (2.0, -1.0),
                          (0.0, 0.0)])

        # Construct a polygon, exterior ring only
        polygon = Polygon(coords)
        self.assertEqual(len(polygon.exterior.coords), 5)

        # Ring Access
        self.assertIsInstance(polygon.exterior, LinearRing)
        ring = polygon.exterior
        self.assertEqual(len(ring.coords), 5)
        self.assertEqual(ring.coords[0], ring.coords[4])
        self.assertEqual(ring.coords[0], (0., 0.))
        self.assertTrue(ring.is_ring)
        self.assertEqual(len(polygon.interiors), 0)

        # Create a new polygon from WKB
        data = polygon.wkb
        polygon = None
        ring = None
        polygon = load_wkb(data)
        ring = polygon.exterior
        self.assertEqual(len(ring.coords), 5)
        self.assertEqual(ring.coords[0], ring.coords[4])
        self.assertEqual(ring.coords[0], (0., 0.))
        self.assertTrue(ring.is_ring)
        polygon = None

        # Interior rings (holes)
        polygon = Polygon(coords, [((0.25, 0.25), (0.25, 0.5),
                                    (0.5, 0.5), (0.5, 0.25))])
        self.assertEqual(len(polygon.exterior.coords), 5)
        self.assertEqual(len(polygon.interiors[0].coords), 5)
        with self.assertRaises(IndexError):  # index out of range
            polygon.interiors[1]

        # Test from another Polygon
        copy = Polygon(polygon)
        self.assertEqual(len(polygon.exterior.coords), 5)
        self.assertEqual(len(polygon.interiors[0].coords), 5)
        with self.assertRaises(IndexError):  # index out of range
            polygon.interiors[1]

        # Coordinate getters and setters raise exceptions
        self.assertRaises(NotImplementedError, polygon._get_coords)
        with self.assertRaises(NotImplementedError):
            polygon.coords

        # Geo interface
        self.assertEqual(
            polygon.__geo_interface__,
            {'type': 'Polygon',
             'coordinates': (((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (2.0, -1.0),
                             (0.0, 0.0)), ((0.25, 0.25), (0.25, 0.5),
                             (0.5, 0.5), (0.5, 0.25), (0.25, 0.25)))})

        # Adapter
        hole_coords = [((0.25, 0.25), (0.25, 0.5), (0.5, 0.5), (0.5, 0.25))]
        pa = asPolygon(coords, hole_coords)
        self.assertEqual(len(pa.exterior.coords), 5)
        self.assertEqual(len(pa.interiors), 1)
        self.assertEqual(len(pa.interiors[0].coords), 5)

        # Test Non-operability of Null rings
        r_null = LinearRing()
        self.assertEqual(r_null.wkt, 'GEOMETRYCOLLECTION EMPTY')
        self.assertEqual(r_null.length, 0.0)

        # Check that we can set coordinates of a null geometry
        r_null.coords = [(0, 0), (1, 1), (1, 0)]
        self.assertAlmostEqual(r_null.length, 3.414213562373095)

        # Error handling
        with self.assertRaises(ValueError):
            # A LinearRing must have at least 3 coordinate tuples
            Polygon([[1, 2], [2, 3]])