github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/geo/geomfn/buffer.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  	"strconv"
    15  	"strings"
    16  
    17  	"github.com/cockroachdb/cockroach/pkg/geo"
    18  	"github.com/cockroachdb/cockroach/pkg/geo/geos"
    19  	"github.com/cockroachdb/errors"
    20  )
    21  
    22  // BufferParams is a wrapper around the geos.BufferParams.
    23  type BufferParams struct {
    24  	p geos.BufferParams
    25  }
    26  
    27  // MakeDefaultBufferParams returns the default BufferParams/
    28  func MakeDefaultBufferParams() BufferParams {
    29  	return BufferParams{
    30  		p: geos.BufferParams{
    31  			EndCapStyle:      geos.BufferParamsEndCapStyleRound,
    32  			JoinStyle:        geos.BufferParamsJoinStyleRound,
    33  			SingleSided:      false,
    34  			QuadrantSegments: 8,
    35  			MitreLimit:       5.0,
    36  		},
    37  	}
    38  }
    39  
    40  // WithQuadrantSegments returns a copy of the BufferParams with the quadrantSegments set.
    41  func (b BufferParams) WithQuadrantSegments(quadrantSegments int) BufferParams {
    42  	ret := b
    43  	ret.p.QuadrantSegments = quadrantSegments
    44  	return ret
    45  }
    46  
    47  // ParseBufferParams parses the given buffer params from a SQL string into
    48  // the BufferParams form.
    49  // The string must be of the same format as specified by https://postgis.net/docs/ST_Buffer.html.
    50  // Returns the BufferParams, as well as the modified distance.
    51  func ParseBufferParams(s string, distance float64) (BufferParams, float64, error) {
    52  	p := MakeDefaultBufferParams()
    53  	fields := strings.Fields(s)
    54  	for _, field := range fields {
    55  		fParams := strings.Split(field, "=")
    56  		if len(fParams) != 2 {
    57  			return BufferParams{}, 0, errors.Newf("unknown buffer parameter: %s", fParams)
    58  		}
    59  		f, val := fParams[0], fParams[1]
    60  		switch strings.ToLower(f) {
    61  		case "quad_segs":
    62  			valInt, err := strconv.ParseInt(val, 10, 64)
    63  			if err != nil {
    64  				return BufferParams{}, 0, errors.Wrapf(err, "invalid int for %s: %s", f, val)
    65  			}
    66  			p.p.QuadrantSegments = int(valInt)
    67  		case "endcap":
    68  			switch strings.ToLower(val) {
    69  			case "round":
    70  				p.p.EndCapStyle = geos.BufferParamsEndCapStyleRound
    71  			case "flat", "butt":
    72  				p.p.EndCapStyle = geos.BufferParamsEndCapStyleFlat
    73  			case "square":
    74  				p.p.EndCapStyle = geos.BufferParamsEndCapStyleSquare
    75  			default:
    76  				return BufferParams{}, 0, errors.Newf("unknown endcap: %s (accepted: round, flat, square)", val)
    77  			}
    78  		case "join":
    79  			switch strings.ToLower(val) {
    80  			case "round":
    81  				p.p.JoinStyle = geos.BufferParamsJoinStyleRound
    82  			case "mitre", "miter":
    83  				p.p.JoinStyle = geos.BufferParamsJoinStyleMitre
    84  			case "bevel":
    85  				p.p.JoinStyle = geos.BufferParamsJoinStyleBevel
    86  			default:
    87  				return BufferParams{}, 0, errors.Newf("unknown join: %s (accepted: round, mitre, bevel)", val)
    88  			}
    89  		case "mitre_limit", "miter_limit":
    90  			valFloat, err := strconv.ParseFloat(val, 64)
    91  			if err != nil {
    92  				return BufferParams{}, 0, errors.Wrapf(err, "invalid float for %s: %s", f, val)
    93  			}
    94  			p.p.MitreLimit = valFloat
    95  		case "side":
    96  			switch strings.ToLower(val) {
    97  			case "both":
    98  				p.p.SingleSided = false
    99  			case "left":
   100  				p.p.SingleSided = true
   101  			case "right":
   102  				p.p.SingleSided = true
   103  				distance *= -1
   104  			default:
   105  				return BufferParams{}, 0, errors.Newf("unknown side: %s (accepted: both, left, right)", val)
   106  			}
   107  		default:
   108  			return BufferParams{}, 0, errors.Newf("unknown field: %s (accepted fields: quad_segs, endcap, join, mitre_limit, side)", f)
   109  		}
   110  	}
   111  	return p, distance, nil
   112  }
   113  
   114  // Buffer buffers a given Geometry by the supplied parameters.
   115  func Buffer(g *geo.Geometry, params BufferParams, distance float64) (*geo.Geometry, error) {
   116  	bufferedGeom, err := geos.Buffer(g.EWKB(), params.p, distance)
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  	return geo.ParseGeometryFromEWKB(bufferedGeom)
   121  }