github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/geo/summary.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 geo
    12  
    13  import (
    14  	"fmt"
    15  	"strings"
    16  
    17  	"github.com/cockroachdb/cockroach/pkg/geo/geopb"
    18  	"github.com/cockroachdb/errors"
    19  	"github.com/twpayne/go-geom"
    20  )
    21  
    22  // Summary returns a text summary of the contents of the geometry type.
    23  //
    24  // Flags shown square brackets after the geometry type have the following meaning:
    25  // M: has M coordinate
    26  // Z: has Z coordinate
    27  // B: has a cached bounding box
    28  // G: is geography
    29  // S: has spatial reference system
    30  func Summary(t geom.T, shape geopb.Shape, isGeography bool) (string, error) {
    31  	return summary(t, shape, isGeography, 0)
    32  }
    33  
    34  func summary(
    35  	t geom.T, shape geopb.Shape, isGeography bool, offset int,
    36  ) (summaryLine string, err error) {
    37  	f, err := summaryFlag(t, isGeography)
    38  	if err != nil {
    39  		return "", err
    40  	}
    41  
    42  	summaryLine += strings.Repeat(" ", offset)
    43  	switch t := t.(type) {
    44  	case *geom.Point:
    45  		return summaryLine + fmt.Sprintf("%s[%s]", shape.String(), f), nil
    46  	case *geom.LineString:
    47  		return summaryLine + fmt.Sprintf("%s[%s] with %d points", shape.String(), f, t.NumCoords()), nil
    48  	case *geom.Polygon:
    49  		numLinearRings := t.NumLinearRings()
    50  
    51  		summaryLine += fmt.Sprintf("%s[%s] with %d ring", shape.String(), f, t.NumLinearRings())
    52  		if numLinearRings > 1 {
    53  			summaryLine += "s"
    54  		}
    55  
    56  		for i := 0; i < numLinearRings; i++ {
    57  			ring := t.LinearRing(i)
    58  			summaryLine += fmt.Sprintf("\n   ring %d has %d points", i, ring.NumCoords())
    59  		}
    60  
    61  		return summaryLine, nil
    62  	case *geom.MultiPoint:
    63  		numPoints := t.NumPoints()
    64  
    65  		summaryLine += fmt.Sprintf("%s[%s] with %d element", shape.String(), f, numPoints)
    66  		if 1 < numPoints {
    67  			summaryLine += "s"
    68  		}
    69  
    70  		for i := 0; i < numPoints; i++ {
    71  			point := t.Point(i)
    72  			line, err := summary(point, geopb.Shape_Point, isGeography, offset+2)
    73  			if err != nil {
    74  				return "", err
    75  			}
    76  
    77  			summaryLine += "\n" + line
    78  		}
    79  
    80  		return summaryLine, nil
    81  	case *geom.MultiLineString:
    82  		numLineStrings := t.NumLineStrings()
    83  
    84  		summaryLine += fmt.Sprintf("%s[%s] with %d element", shape.String(), f, numLineStrings)
    85  		if 1 < numLineStrings {
    86  			summaryLine += "s"
    87  		}
    88  
    89  		for i := 0; i < numLineStrings; i++ {
    90  			lineString := t.LineString(i)
    91  			line, err := summary(lineString, geopb.Shape_LineString, isGeography, offset+2)
    92  			if err != nil {
    93  				return "", err
    94  			}
    95  
    96  			summaryLine += "\n" + line
    97  		}
    98  
    99  		return summaryLine, nil
   100  	case *geom.MultiPolygon:
   101  		numPolygons := t.NumPolygons()
   102  
   103  		summaryLine += fmt.Sprintf("%s[%s] with %d element", shape.String(), f, numPolygons)
   104  		if 1 < numPolygons {
   105  			summaryLine += "s"
   106  		}
   107  
   108  		for i := 0; i < numPolygons; i++ {
   109  			polygon := t.Polygon(i)
   110  			line, err := summary(polygon, geopb.Shape_Polygon, isGeography, offset+2)
   111  			if err != nil {
   112  				return "", err
   113  			}
   114  
   115  			summaryLine += "\n" + line
   116  		}
   117  
   118  		return summaryLine, nil
   119  	case *geom.GeometryCollection:
   120  		numGeoms := t.NumGeoms()
   121  
   122  		summaryLine += fmt.Sprintf("%s[%s] with %d element", shape.String(), f, numGeoms)
   123  		if 1 < numGeoms {
   124  			summaryLine += "s"
   125  		}
   126  
   127  		for i := 0; i < numGeoms; i++ {
   128  			g := t.Geom(i)
   129  			gShape, err := geopbShape(g)
   130  			if err != nil {
   131  				return "", err
   132  			}
   133  
   134  			line, err := summary(g, gShape, isGeography, offset+2)
   135  			if err != nil {
   136  				return "", err
   137  			}
   138  
   139  			summaryLine += "\n" + line
   140  		}
   141  
   142  		return summaryLine, nil
   143  	default:
   144  		return "", errors.Newf("unsupported geom type: %T", t)
   145  	}
   146  }
   147  
   148  func summaryFlag(t geom.T, isGeography bool) (f string, err error) {
   149  	layout := t.Layout()
   150  	if layout.MIndex() != -1 {
   151  		f += "M"
   152  	}
   153  
   154  	if layout.ZIndex() != -1 {
   155  		f += "Z"
   156  	}
   157  
   158  	bbox, err := BoundingBoxFromGeom(t)
   159  	if err != nil {
   160  		return "", err
   161  	}
   162  
   163  	if bbox != nil {
   164  		f += "B"
   165  	}
   166  
   167  	if geopb.SRID(t.SRID()) != geopb.UnknownSRID {
   168  		f += "S"
   169  	}
   170  
   171  	if isGeography {
   172  		f += "G"
   173  	}
   174  
   175  	return f, nil
   176  }