/** * Returns the singleton instance of geometric class of the given type. * * @param string $type type of the geometric object * * @return GISGeometry the singleton instance of geometric class * of the given type * * @access public * @static */ public static function factory($type) { $type_lower = strtolower($type); $file = './libraries/gis/GIS' . ucfirst($type_lower) . '.php'; if (!PMA_isValid($type_lower, PMA\libraries\Util::getGISDatatypes()) || !file_exists($file)) { return false; } if (include_once $file) { switch (strtoupper($type)) { case 'MULTIPOLYGON': return GISMultipolygon::singleton(); case 'POLYGON': return GISPolygon::singleton(); case 'MULTIPOINT': return GISMultipoint::singleton(); case 'POINT': return GISPoint::singleton(); case 'MULTILINESTRING': return GISMultilinestring::singleton(); case 'LINESTRING': return GISLinestring::singleton(); case 'GEOMETRYCOLLECTION': return GISGeometrycollection::singleton(); default: return false; } } else { return false; } }
/** * Returns the singleton instance of geometric class of the given type. * * @param string $type type of the geometric object * * @return GISGeometry the singleton instance of geometric class * of the given type * * @access public * @static */ public static function factory($type) { switch (strtoupper($type)) { case 'MULTIPOLYGON': return GISMultipolygon::singleton(); case 'POLYGON': return GISPolygon::singleton(); case 'MULTIPOINT': return GISMultipoint::singleton(); case 'POINT': return GISPoint::singleton(); case 'MULTILINESTRING': return GISMultilinestring::singleton(); case 'LINESTRING': return GISLinestring::singleton(); case 'GEOMETRYCOLLECTION': return GISGeometrycollection::singleton(); default: return false; } }
/** * Returns a point that is guaranteed to be on the surface of the ring. * (for simple closed rings) * * @param array $ring array of points forming the ring * * @return array|void a point on the surface of the ring * @access public * @static */ public static function getPointOnSurface($ring) { // Find two consecutive distinct points. for ($i = 0, $nb = count($ring) - 1; $i < $nb; $i++) { if ($ring[$i]['y'] != $ring[$i + 1]['y']) { $x0 = $ring[$i]['x']; $x1 = $ring[$i + 1]['x']; $y0 = $ring[$i]['y']; $y1 = $ring[$i + 1]['y']; break; } } if (!isset($x0)) { return false; } // Find the mid point $x2 = ($x0 + $x1) / 2; $y2 = ($y0 + $y1) / 2; // Always keep $epsilon < 1 to go with the reduction logic down here $epsilon = 0.1; $denominator = sqrt(Util::pow($y1 - $y0, 2) + Util::pow($x0 - $x1, 2)); $pointA = array(); $pointB = array(); while (true) { // Get the points on either sides of the line // with a distance of epsilon to the mid point $pointA['x'] = $x2 + $epsilon * ($y1 - $y0) / $denominator; $pointA['y'] = $y2 + ($pointA['x'] - $x2) * ($x0 - $x1) / ($y1 - $y0); $pointB['x'] = $x2 + $epsilon * ($y1 - $y0) / (0 - $denominator); $pointB['y'] = $y2 + ($pointB['x'] - $x2) * ($x0 - $x1) / ($y1 - $y0); // One of the points should be inside the polygon, // unless epsilon chosen is too large if (GISPolygon::isPointInsidePolygon($pointA, $ring)) { return $pointA; } if (GISPolygon::isPointInsidePolygon($pointB, $ring)) { return $pointB; } //If both are outside the polygon reduce the epsilon and //recalculate the points(reduce exponentially for faster convergence) $epsilon = Util::pow($epsilon, 2); if ($epsilon == 0) { return false; } } }
/** * test case for isOuterRing() method * * @param array $ring coordinates of the points in a ring * * @return void * @dataProvider providerForIsOuterRing */ public function testIsOuterRing($ring) { $this->assertTrue($this->object->isOuterRing($ring)); }
/** * Generate the WKT for the data from ESRI shape files. * * @param array $row_data GIS data * * @return string the WKT for the data from ESRI shape files * @access public */ public function getShape($row_data) { // Determines whether each line ring is an inner ring or an outer ring. // If it's an inner ring get a point on the surface which can be used to // correctly classify inner rings to their respective outer rings. foreach ($row_data['parts'] as $i => $ring) { $row_data['parts'][$i]['isOuter'] = GISPolygon::isOuterRing($ring['points']); } // Find points on surface for inner rings foreach ($row_data['parts'] as $i => $ring) { if (!$ring['isOuter']) { $row_data['parts'][$i]['pointOnSurface'] = GISPolygon::getPointOnSurface($ring['points']); } } // Classify inner rings to their respective outer rings. foreach ($row_data['parts'] as $j => $ring1) { if ($ring1['isOuter']) { continue; } foreach ($row_data['parts'] as $k => $ring2) { if (!$ring2['isOuter']) { continue; } // If the pointOnSurface of the inner ring // is also inside the outer ring if (GISPolygon::isPointInsidePolygon($ring1['pointOnSurface'], $ring2['points'])) { if (!isset($ring2['inner'])) { $row_data['parts'][$k]['inner'] = array(); } $row_data['parts'][$k]['inner'][] = $j; } } } $wkt = 'MULTIPOLYGON('; // for each polygon foreach ($row_data['parts'] as $ring) { if (!$ring['isOuter']) { continue; } $wkt .= '('; // start of polygon $wkt .= '('; // start of outer ring foreach ($ring['points'] as $point) { $wkt .= $point['x'] . ' ' . $point['y'] . ','; } $wkt = mb_substr($wkt, 0, mb_strlen($wkt) - 1); $wkt .= ')'; // end of outer ring // inner rings if any if (isset($ring['inner'])) { foreach ($ring['inner'] as $j) { $wkt .= ',('; // start of inner ring foreach ($row_data['parts'][$j]['points'] as $innerPoint) { $wkt .= $innerPoint['x'] . ' ' . $innerPoint['y'] . ','; } $wkt = mb_substr($wkt, 0, mb_strlen($wkt) - 1); $wkt .= ')'; // end of inner ring } } $wkt .= '),'; // end of polygon } $wkt = mb_substr($wkt, 0, mb_strlen($wkt) - 1); $wkt .= ')'; // end of multipolygon return $wkt; }