func orderCCWRingFirst(rings []LineString) {
	for i, r := range rings {
		if ccw := signedAreaOfLinearRing(r, nil) > 0; ccw {
			rings[i], rings[0] = rings[0], rings[i]
			return
		}
	}
}
orderCCWRingFirst reorders rings such that if it contains at least one CCW
ring, then a CCW ring is the first element.
package geom
import "fmt"
func (d *doublyConnectedEdgeList) extractGeometry(include func([2]label) bool) (Geometry, error) {
	areals, err := d.extractPolygons(include)
	if err != nil {
		return Geometry{}, err
	}
	linears, err := d.extractLineStrings(include)
	if err != nil {
		return Geometry{}, err
	}
	points, err := d.extractPoints(include)
	if err != nil {
		return Geometry{}, err
	}
	switch {
	case len(areals) > 0 && len(linears) == 0 && len(points) == 0:
		if len(areals) == 1 {
			return areals[0].AsGeometry(), nil
		}
		mp, err := NewMultiPolygon(areals)
		if err != nil {
			return Geometry{}, wrap(err, "could not extract areal geometry from DCEL")
		}
		return mp.AsGeometry(), nil
	case len(areals) == 0 && len(linears) > 0 && len(points) == 0:
		if len(linears) == 1 {
			return linears[0].AsGeometry(), nil
		}
		return NewMultiLineString(linears).AsGeometry(), nil
	case len(areals) == 0 && len(linears) == 0 && len(points) > 0:
		if len(points) == 1 {
			return points[0].AsGeometry(), nil
		}
		return NewMultiPoint(points).AsGeometry(), nil
	default:
		geoms := make([]Geometry, 0, len(areals)+len(linears)+len(points))
		for _, poly := range areals {
			geoms = append(geoms, poly.AsGeometry())
		}
		for _, ls := range linears {
			geoms = append(geoms, ls.AsGeometry())
		}
		for _, pt := range points {
			geoms = append(geoms, pt.AsGeometry())
		}
		return NewGeometryCollection(geoms).AsGeometry(), nil
	}
}
func (d *doublyConnectedEdgeList) extractPolygons(include func([2]label) bool) ([]Polygon, error) {
	var polys []Polygon
	for _, face := range d.faces {
		if !include(face.labels) || face.extracted {
			continue
		}
		facesInPoly := findFacesMakingPolygon(include, face)
		var rings []LineString
		seen := make(map[*halfEdgeRecord]bool)
		for f := range facesInPoly {
			f.extracted = true
			forEachEdge(f.cycle, func(edge *halfEdgeRecord) {
				edge.extracted = true
				edge.twin.extracted = true
				edge.origin.extracted = true
				if seen[edge] {
					return
				}
				if include(edge.twin.incident.labels) {
					seen[edge] = true
					return
				}
				seq := extractPolygonBoundary(facesInPoly, edge, seen)
				ring, err := NewLineString(seq)
				if err != nil {
					panic(fmt.Sprintf("could not create LineString: %v", err))
				}
				rings = append(rings, ring)
			})
		}
		orderCCWRingFirst(rings)
		poly, err := NewPolygon(rings)
		if err != nil {
			return nil, err
		}
		polys = append(polys, poly)
	}
	return polys, nil
}
func extractPolygonBoundary(faceSet map[*faceRecord]bool, start *halfEdgeRecord, seen map[*halfEdgeRecord]bool) Sequence {
	var coords []float64
	e := start
	for {
		seen[e] = true
		xy := e.origin.coords
		coords = append(coords, xy.X, xy.Y)
		for _, xy := range e.intermediate {
			coords = append(coords, xy.X, xy.Y)
		}
		e = e.twin.prev.twin
		for !faceSet[e.incident] {
			e = e.prev.twin
		}
		if e == start {
			break
		}
	}
	coords = append(coords, coords[:2]...)
	return NewSequence(coords, DimXY)
}
func findFacesMakingPolygon(include func([2]label) bool, start *faceRecord) map[*faceRecord]bool {
	expanded := make(map[*faceRecord]bool)
	toExpand := make(map[*faceRecord]bool)
	toExpand[start] = true
	for len(toExpand) > 0 {
		var popped *faceRecord
		for f := range toExpand {
			delete(toExpand, f)
			popped = f
			break
		}
		adj := adjacentFaces(popped)
		expanded[popped] = true
		for _, f := range adj {
			if !include(f.labels) {
				continue
			}
			if expanded[f] {
				continue
			}
			if toExpand[f] {
				continue
			}
			toExpand[f] = true
		}
	}
	return expanded
}