github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/geo/geomfn/linear_reference.go (about) 1 // Copyright 2020 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package geomfn 12 13 import ( 14 "github.com/cockroachdb/cockroach/pkg/geo" 15 "github.com/cockroachdb/cockroach/pkg/geo/geos" 16 "github.com/cockroachdb/errors" 17 "github.com/twpayne/go-geom" 18 "github.com/twpayne/go-geom/encoding/ewkb" 19 ) 20 21 // LineInterpolatePoints returns one or more points along the given 22 // LineString which are at an integral multiples of given fraction of 23 // LineString's total length. When repeat is set to false, it returns 24 // the first point. 25 func LineInterpolatePoints(g *geo.Geometry, fraction float64, repeat bool) (*geo.Geometry, error) { 26 if fraction < 0 || fraction > 1 { 27 return nil, errors.Newf("fraction %f should be within [0 1] range", fraction) 28 } 29 geomRepr, err := g.AsGeomT() 30 if err != nil { 31 return nil, err 32 } 33 // Empty geometries do not react well in GEOS, so we have to 34 // convert and check beforehand. 35 // Remove after #49209 is resolved. 36 if geomRepr.Empty() { 37 return geo.NewGeometryFromGeom(geom.NewPointEmpty(geom.XY)) 38 } 39 switch geomRepr := geomRepr.(type) { 40 case *geom.LineString: 41 // In case fraction is greater than 0.5 or equal to 0 or repeat is false, 42 // then we will have only one interpolated point. 43 lengthOfLineString := geomRepr.Length() 44 if repeat && fraction <= 0.5 && fraction != 0 { 45 numberOfInterpolatedPoints := int(1 / fraction) 46 interpolatedPoints := geom.NewMultiPoint(geom.XY).SetSRID(geomRepr.SRID()) 47 for pointInserted := 1; pointInserted <= numberOfInterpolatedPoints; pointInserted++ { 48 pointEWKB, err := geos.InterpolateLine(g.EWKB(), float64(pointInserted)*fraction*lengthOfLineString) 49 if err != nil { 50 return nil, err 51 } 52 point, err := ewkb.Unmarshal(pointEWKB) 53 if err != nil { 54 return nil, err 55 } 56 err = interpolatedPoints.Push(point.(*geom.Point)) 57 if err != nil { 58 return nil, err 59 } 60 } 61 return geo.NewGeometryFromGeom(interpolatedPoints) 62 } 63 interpolatedPointEWKB, err := geos.InterpolateLine(g.EWKB(), fraction*lengthOfLineString) 64 if err != nil { 65 return nil, err 66 } 67 return geo.ParseGeometryFromEWKB(interpolatedPointEWKB) 68 default: 69 return nil, errors.Newf("geometry %s should be LineString", g.Shape()) 70 } 71 }