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