1<?php 2 3namespace geoPHP\Geometry; 4 5use geoPHP\geoPHP; 6 7/** 8 * GeometryCollection: A heterogeneous collection of geometries 9 */ 10class GeometryCollection extends MultiGeometry 11{ 12 13 /** 14 * @param Geometry[] $components Array of geometries. Components of GeometryCollection can be 15 * any of valid Geometry types, including empty geometry 16 * 17 * @throws \Exception 18 */ 19 public function __construct($components = []) 20 { 21 parent::__construct($components, true); 22 } 23 24 public function geometryType() 25 { 26 return Geometry::GEOMETRY_COLLECTION; 27 } 28 29 /** 30 * @return int Returns the highest spatial dimension of components 31 */ 32 public function dimension() 33 { 34 $dimension = 0; 35 foreach ($this->getComponents() as $component) { 36 if ($component->dimension() > $dimension) { 37 $dimension = $component->dimension(); 38 } 39 } 40 return $dimension; 41 } 42 43 /** 44 * Not valid for this geometry type 45 * @return null 46 */ 47 public function isSimple() 48 { 49 return null; 50 } 51 52 /** 53 * In a GeometryCollection, the centroid is equal to the centroid of 54 * the set of component Geometries of highest dimension 55 * (since the lower-dimension geometries contribute zero "weight" to the centroid). 56 * 57 * @return Point 58 * @throws \Exception 59 */ 60 public function centroid() 61 { 62 if ($this->isEmpty()) { 63 return new Point(); 64 } 65 66 if ($this->getGeos()) { 67 // @codeCoverageIgnoreStart 68 /** @noinspection PhpUndefinedMethodInspection */ 69 return geoPHP::geosToGeometry($this->getGeos()->centroid()); 70 // @codeCoverageIgnoreEnd 71 } 72 73 $geometries = $this->explodeGeometries(); 74 75 $highestDimension = 0; 76 foreach ($geometries as $geometry) { 77 if ($geometry->dimension() > $highestDimension) { 78 $highestDimension = $geometry->dimension(); 79 } 80 if ($highestDimension === 2) { 81 break; 82 } 83 } 84 85 $highestDimensionGeometries = []; 86 foreach ($geometries as $geometry) { 87 if ($geometry->dimension() === $highestDimension) { 88 $highestDimensionGeometries[] = $geometry; 89 } 90 } 91 92 $reducedGeometry = geoPHP::geometryReduce($highestDimensionGeometries); 93 if ($reducedGeometry->geometryType() === Geometry::GEOMETRY_COLLECTION) { 94 throw new \Exception('Internal error: GeometryCollection->centroid() calculation failed.'); 95 } 96 return $reducedGeometry->centroid(); 97 } 98 99 /** 100 * Returns every sub-geometry as a multidimensional array 101 * 102 * Because geometryCollections are heterogeneous we need to specify which type of geometries they contain. 103 * We need to do this because, for example, there would be no way to tell the difference between a 104 * MultiPoint or a LineString, since they share the same structure (collection 105 * of points). So we need to call out the type explicitly. 106 * 107 * @return array 108 */ 109 public function asArray() 110 { 111 $array = []; 112 foreach ($this->getComponents() as $component) { 113 $array[] = [ 114 'type' => $component->geometryType(), 115 'components' => $component->asArray(), 116 ]; 117 } 118 return $array; 119 } 120 121 /** 122 * @return Geometry[]|Collection[] 123 */ 124 public function explodeGeometries() 125 { 126 $geometries = []; 127 foreach ($this->components as $component) { 128 if ($component->geometryType() === Geometry::GEOMETRY_COLLECTION) { 129 /** @var GeometryCollection $component */ 130 $geometries = array_merge($geometries, $component->explodeGeometries()); 131 } else { 132 $geometries[] = $component; 133 } 134 } 135 return $geometries; 136 } 137 138 // Not valid for this geometry 139 public function boundary() 140 { 141 return null; 142 } 143} 144