func (m MultiLineString) DumpCoordinates() Sequence {
	var n int
	for _, ls := range m.lines {
		n += ls.seq.Length() * m.ctype.Dimension()
	}
	coords := make([]float64, 0, n)
	for _, ls := range m.lines {
		coords = ls.Coordinates().appendAllPoints(coords)
	}
	seq := NewSequence(coords, m.ctype)
	seq.assertNoUnusedCapacity()
	return seq
}
DumpCoordinates returns the coordinates (as a Sequence) that constitute the
MultiLineString.
package geom
import (
	"database/sql/driver"
	"fmt"
	"unsafe"
	"github.com/peterstace/simplefeatures/rtree"
)
type MultiLineString struct {
	lines []LineString
	ctype CoordinatesType
}
func NewMultiLineString(lines []LineString, opts ...ConstructorOption) MultiLineString {
	if len(lines) == 0 {
		return MultiLineString{}
	}
	ctype := DimXYZM
	for _, ls := range lines {
		ctype &= ls.CoordinatesType()
	}
	lines = append([]LineString(nil), lines...)
	for i := range lines {
		lines[i] = lines[i].ForceCoordinatesType(ctype)
	}
	return MultiLineString{lines, ctype}
}
func (m MultiLineString) Type() GeometryType {
	return TypeMultiLineString
}
func (m MultiLineString) AsGeometry() Geometry {
	return Geometry{TypeMultiLineString, unsafe.Pointer(&m)}
}
func (m MultiLineString) NumLineStrings() int {
	return len(m.lines)
}
func (m MultiLineString) LineStringN(n int) LineString {
	return m.lines[n]
}
func (m MultiLineString) AsText() string {
	return string(m.AppendWKT(nil))
}
func (m MultiLineString) AppendWKT(dst []byte) []byte {
	dst = appendWKTHeader(dst, "MULTILINESTRING", m.ctype)
	if len(m.lines) == 0 {
		return appendWKTEmpty(dst)
	}
	dst = append(dst, '(')
	for i, line := range m.lines {
		if i > 0 {
			dst = append(dst, ',')
		}
		dst = line.appendWKTBody(dst)
	}
	return append(dst, ')')
}
func (m MultiLineString) IsSimple() bool {
	for _, ls := range m.lines {
		if !ls.IsSimple() {
			return false
		}
	}
	toRecordID := func(lineStringIdx, seqIdx int) int {
		return int(uint64(lineStringIdx)<<32 | uint64(seqIdx))
	}
	fromRecordID := func(recordID int) (lineStringIdx, seqIdx int) {
		lineStringIdx = int(uint64(recordID) >> 32)
		seqIdx = int((uint64(recordID) << 32) >> 32)
		return
	}
	var numItems int
	for _, ls := range m.lines {
		numItems += max(0, ls.Coordinates().Length()-1)
	}
	items := make([]rtree.BulkItem, 0, numItems)
	for i, ls := range m.lines {
		seq := ls.Coordinates()
		seqLen := seq.Length()
		for j := 0; j < seqLen; j++ {
			ln, ok := getLine(seq, j)
			if !ok {
				continue
			}
			items = append(items, rtree.BulkItem{
				Box:      ln.box(),
				RecordID: toRecordID(i, j),
			})
		}
	}
	tree := rtree.BulkLoad(items)
	for i, ls := range m.lines {
		seq := ls.Coordinates()
		seqLen := seq.Length()
		for j := 0; j < seqLen; j++ {
			ln, ok := getLine(seq, j)
			if !ok {
				continue
			}
			isSimple := true  
			tree.RangeSearch(ln.box(), func(recordID int) error {
				lineStringIdx, seqIdx := fromRecordID(recordID)
				if lineStringIdx == i {
					return nil
				}
				otherLS := m.lines[lineStringIdx]
				other, ok := getLine(otherLS.Coordinates(), seqIdx)
				if !ok {
					panic("could not getLine")
				}
				inter := ln.intersectLine(other)
				if inter.empty {
					return nil
				}
				if inter.ptA != inter.ptB {
					isSimple = false
					return rtree.Stop
				}
				boundary := intersectionOfMultiPointAndMultiPoint(ls.Boundary(), otherLS.Boundary())
				if !hasIntersectionPointWithMultiPoint(inter.ptA.asUncheckedPoint(), boundary) {
					isSimple = false
					return rtree.Stop
				}
				return nil
			})
			if !isSimple {
				return false
			}
		}
	}
	return true
}
func (m MultiLineString) IsEmpty() bool {
	for _, ls := range m.lines {
		if !ls.IsEmpty() {
			return false
		}
	}
	return true
}
func (m MultiLineString) Envelope() Envelope {
	var env Envelope
	for _, ls := range m.lines {
		env = env.ExpandToIncludeEnvelope(ls.Envelope())
	}
	return env
}
func (m MultiLineString) Boundary() MultiPoint {
	counts := make(map[XY]int)
	var uniqueEndpoints []Point
	for _, ls := range m.lines {
		if ls.IsClosed() {
			continue
		}
		for _, pt := range [2]Point{
			ls.StartPoint(),
			ls.EndPoint(),
		} {
			xy, ok := pt.XY()
			if !ok {
				continue
			}
			if counts[xy] == 0 {
				uniqueEndpoints = append(uniqueEndpoints, pt)
			}
			counts[xy]++
		}
	}
	var mod2Points []Point
	for _, pt := range uniqueEndpoints {
		xy, ok := pt.XY()
		if !ok {
			panic("MultiLineString Boundary internal error")
		}
		if counts[xy]%2 == 1 {
			mod2Points = append(mod2Points, pt)
		}
	}
	return NewMultiPoint(mod2Points)
}
func (m MultiLineString) Value() (driver.Value, error) {
	return m.AsBinary(), nil
}
func (m *MultiLineString) Scan(src interface{}) error {
	return scanAsType(src, m, TypeMultiLineString)
}
func (m MultiLineString) AsBinary() []byte {
	return m.AppendWKB(nil)
}
func (m MultiLineString) AppendWKB(dst []byte) []byte {
	marsh := newWKBMarshaller(dst)
	marsh.writeByteOrder()
	marsh.writeGeomType(TypeMultiLineString, m.ctype)
	n := m.NumLineStrings()
	marsh.writeCount(n)
	for i := 0; i < n; i++ {
		ls := m.LineStringN(i)
		marsh.buf = ls.AppendWKB(marsh.buf)
	}
	return marsh.buf
}
func (m MultiLineString) ConvexHull() Geometry {
	return convexHull(m.AsGeometry())
}
func (m MultiLineString) MarshalJSON() ([]byte, error) {
	var dst []byte
	dst = append(dst, `{"type":"MultiLineString","coordinates":`...)
	dst = appendGeoJSONSequences(dst, m.Coordinates())
	dst = append(dst, '}')
	return dst, nil
}
func (m MultiLineString) Coordinates() []Sequence {
	n := m.NumLineStrings()
	seqs := make([]Sequence, n)
	for i := 0; i < n; i++ {
		seqs[i] = m.LineStringN(i).Coordinates()
	}
	return seqs
}
func (m MultiLineString) TransformXY(fn func(XY) XY, opts ...ConstructorOption) (MultiLineString, error) {
	n := m.NumLineStrings()
	transformed := make([]LineString, n)
	for i := 0; i < n; i++ {
		var err error
		transformed[i], err = NewLineString(
			transformSequence(m.LineStringN(i).Coordinates(), fn),
			opts...,
		)
		if err != nil {
			return MultiLineString{}, wrapTransformed(err)
		}
	}
	return NewMultiLineString(transformed, opts...), nil
}
func (m MultiLineString) Length() float64 {
	var sum float64
	for _, ln := range m.lines {
		sum += ln.Length()
	}
	return sum
}
func (m MultiLineString) Centroid() Point {
	var sumXY XY
	var sumLength float64
	for i := 0; i < m.NumLineStrings(); i++ {
		ls := m.LineStringN(i)
		xy, length := sumCentroidAndLengthOfLineString(ls)
		sumXY = sumXY.Add(xy)
		sumLength += length
	}
	if sumLength == 0 {
		return NewEmptyPoint(DimXY)
	}
	return sumXY.Scale(1.0 / sumLength).asUncheckedPoint()
}
func (m MultiLineString) Reverse() MultiLineString {
	linestrings := make([]LineString, len(m.lines))
	for i := 0; i < len(m.lines); i++ {
		linestrings[i] = m.lines[i].Reverse()
	}
	return MultiLineString{linestrings, m.ctype}
}
func (m MultiLineString) CoordinatesType() CoordinatesType {
	return m.ctype
}
func (m MultiLineString) ForceCoordinatesType(newCType CoordinatesType) MultiLineString {
	flat := make([]LineString, len(m.lines))
	for i := range m.lines {
		flat[i] = m.lines[i].ForceCoordinatesType(newCType)
	}
	return MultiLineString{flat, newCType}
}
func (m MultiLineString) Force2D() MultiLineString {
	return m.ForceCoordinatesType(DimXY)
}
func (m MultiLineString) asLines() []line {
	var n int
	numLineStrings := m.NumLineStrings()
	for i := 0; i < numLineStrings; i++ {
		n += max(0, m.LineStringN(i).Coordinates().Length()-1)
	}
	lines := make([]line, 0, n)
	for i := 0; i < numLineStrings; i++ {
		seq := m.LineStringN(i).Coordinates()
		length := seq.Length()
		for j := 0; j < length; j++ {
			ln, ok := getLine(seq, j)
			if ok {
				lines = append(lines, ln)
			}
		}
	}
	return lines
}
func (m MultiLineString) PointOnSurface() Point {
	nearest := newNearestPointAccumulator(m.Centroid())
	for i := 0; i < m.NumLineStrings(); i++ {
		seq := m.LineStringN(i).Coordinates()
		n := seq.Length()
		for j := 1; j < n-1; j++ {
			candidate := seq.GetXY(j).asUncheckedPoint()
			nearest.consider(candidate)
		}
	}
	if !nearest.point.IsEmpty() {
		return nearest.point
	}
	for i := 0; i < m.NumLineStrings(); i++ {
		ls := m.LineStringN(i)
		nearest.consider(ls.StartPoint().Force2D())
		nearest.consider(ls.EndPoint().Force2D())
	}
	return nearest.point
}
func (m MultiLineString) controlPoints() int {
	var sum int
	for _, ls := range m.lines {
		sum += ls.Coordinates().Length()
	}
	return sum
}
func (m MultiLineString) Dump() []LineString {
	lss := make([]LineString, len(m.lines))
	copy(lss, m.lines)
	return lss
}