func (s *Slide) Do() (*Result, error) {
	if len(s.Geometry) == 0 {
		return nil, errors.New("slide: please provide at least one path")
	}
	if len(s.Geometry) > 1 {
		return nil, errors.New("slide: currently the sliding of only one path is supported")
	}
	if s.Geometry[0] == nil {
		return nil, errors.New("slide: geometry[0] is nil")
	}
	if s.Geometry[0].Length() < 2 {
		return nil, errors.New("slide: path less than 2 points")
	}
	start := time.Now()
	if s.Goroutines < 1 {
		s.Goroutines = 1
	}
	s.latLngBound = s.Geometry[0].Bound()
	for i := 1; i < len(s.Geometry); i++ {
		s.latLngBound.Union(s.Geometry[i].Bound())
	}
	scaleFactor := geo.MercatorScaleFactor(s.latLngBound.Center().Lat())
	for i := range s.Geometry {
		s.Geometry[i].Transform(geo.Mercator.Project)
		distance := s.Geometry[0].Distance()
		count := int(math.Ceil(distance / (s.ResampleInterval * scaleFactor)))
		s.Geometry[i].Resample(count + 3)
	}
	result, err := s.refine()
	if err != nil {
		return nil, err
	}
	for i, p := range result.CorrectedGeometry {
		p.Transform(geo.Mercator.Inverse)
		if s.GeoReducer != nil {
			result.CorrectedGeometry[i] = s.GeoReducer.GeoReduce(p)
		} else {
			result.CorrectedGeometry[i] = p
		}
	}
	for i := range result.IntermediateGeometry {
		for j, p := range result.IntermediateGeometry[i] {
			p.Transform(geo.Mercator.Inverse)
			if s.GeoReducer != nil {
				result.IntermediateGeometry[i][j] = s.GeoReducer.GeoReduce(p)
			} else {
				result.IntermediateGeometry[i][j] = p
			}
		}
	}
	result.Runtime = time.Since(start)
	return result, nil
}
Do performs the slide algorithm which includes the following:
- transform geometries into EPSG:3857 and resample
- iterate and refine path
- transform the result back into EPSG:4326
package slide
import (
	"errors"
	"math"
	"runtime"
	"time"
	"github.com/paulmach/go.geo"
	geo_reducers "github.com/paulmach/go.geo/reducers"
	slide_reducers "github.com/paulmach/slide/reducers"
)
const (
	DefaultMinLoops         = 100
	DefaultMaxLoops         = 4000
	DefaultThresholdEpsilon = 0.0005
	DefaultResampleInterval = 5.0  
)
type Slide struct {
	Geometry   []*geo.Path
	Surfacer   Surfacer
	GeoReducer geo.GeoReducer
	MinLoops   int  
	MaxLoops   int  
	Goroutines int  
	ThresholdEpsilon float64
	ResampleInterval float64
	GradientScale float64
	DistanceScale float64
	AngleScale    float64
	MomentumScale float64
	GradientContributionFunc func(surfacer Surfacer, point *geo.Point, scale float64) *geo.Point
	DistanceContributionFunc func(path *geo.Path, index int, scale float64) *geo.Point
	AngleContributionFunc    func(path *geo.Path, index int, scale float64) *geo.Point
	DepthBasedReduction bool
	NumberIntermediateGeometries int
	latLngBound *geo.Bound
}
type Result struct {
	CorrectedGeometry    []*geo.Path
	IntermediateGeometry [][]*geo.Path
	LoopsCompleted       int
	LastLoopError        float64
	LastLoopScore        float64
	Runtime              time.Duration
}
func New(geometry []*geo.Path, surfacer Surfacer) *Slide {
	suggested := surfacer.SuggestedOptions()
	return &Slide{
		Geometry:   geometry,
		Surfacer:   surfacer,
		GeoReducer: slide_reducers.NewTrim(geo_reducers.NewDouglasPeucker(1.0)),
		MinLoops:         DefaultMinLoops,
		MaxLoops:         DefaultMaxLoops,
		ThresholdEpsilon: DefaultThresholdEpsilon,
		Goroutines:       runtime.NumCPU(),
		ResampleInterval: DefaultResampleInterval,
		GradientScale: suggested.GradientScale,
		DistanceScale: suggested.DistanceScale,
		AngleScale:    suggested.AngleScale,
		MomentumScale: suggested.MomentumScale,
		GradientContributionFunc: gradientContribution,
		DistanceContributionFunc: distanceContribution,
		AngleContributionFunc:    angleContribution,
		DepthBasedReduction: suggested.DepthBasedReduction,
	}
}