Correctly import multipolygons from ESRI shape files

This commit is contained in:
Madhura Jayaratne 2011-07-08 18:32:35 +05:30
parent 28519cf25b
commit a58bb10e18
3 changed files with 210 additions and 5 deletions

View File

@ -359,7 +359,115 @@ class PMA_GIS_Multipolygon extends PMA_GIS_Geometry
return $wkt;
}
/** Generate parameters for the GIS data editor from the value of the GIS column.
/**
* Generate the WKT for the data from ESRI shape files.
*
* @param array $row_data GIS data
*
* @return the WKT for the data from ESRI shape files
*/
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.
require_once './libraries/gis/pma_gis_polygon.php';
foreach ($row_data['parts'] as $i => $ring) {
$row_data['parts'][$i]['isOuter'] = PMA_GIS_Polygon::isOuterRing($ring['points']);
}
$this->getPointsOnSurface($row_data['parts']);
// Classify inner rings to their respective outer rings.
foreach ($row_data['parts'] as $j => $ring1) {
if (! $ring1['isOuter']) {
foreach ($row_data['parts'] as $k => $ring2) {
if ($ring2['isOuter']) {
// If the pointOnSurface of the inner ring is also inside the outer ring
if (PMA_GIS_Polygon::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']) {
$wkt .= '('; // start of polygon
$wkt .= '('; // start of outer ring
foreach($ring['points'] as $point) {
$wkt .= $point['x'] . ' ' . $point['y'] . ',';
}
$wkt = substr($wkt, 0, 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 = substr($wkt, 0, strlen($wkt) - 1);
$wkt .= ')'; // end of inner ring
}
}
$wkt .= '),'; // end of polygon
}
}
$wkt = substr($wkt, 0, strlen($wkt) - 1);
$wkt .= ')'; // end of multipolygon
return $wkt;
}
/**
* Attach to each ring a point on its surface.
*
* @param array $rings
*/
private function getPointsOnSurface(&$rings)
{
$sql = 'SELECT ';
$inner_rings = false;
foreach ($rings as $i => $ring) {
if (! $ring['isOuter']) {
$inner_rings = true;
// gis_data[0]['MULTIPOLYGON'][0][0] will have $ring
$gis_data = array(array('MULTIPOLYGON' => array(array($ring['points']))));
$gis_data[0]['MULTIPOLYGON'][0][0]['no_of_points'] = count($ring['points']);
$geom = $this->generateWkt($gis_data, 0);
$sql .= 'AsText(PointOnSurface(GeomFromText("' . $geom . '"))) as `P' . $i . '`, ';
}
}
if (! $inner_rings) {
return;
}
$sql = substr($sql, 0, strlen($sql) - 2);
$result = PMA_DBI_fetch_result($sql);
require_once './libraries/gis/pma_gis_point.php';
$point = PMA_GIS_Point::singleton();
foreach ($rings as $i => $ring) {
if (! $ring['isOuter']) {
$param = $point->generateParams($result[0]['P' . $i], 0);
$rings[$i]['pointOnSurface'] = $param[0]['POINT'];
}
}
}
/**
* Generate parameters for the GIS data editor from the value of the GIS column.
*
* @param string $value of the GIS column
* @param index $index of the geometry

View File

@ -324,6 +324,100 @@ class PMA_GIS_Polygon extends PMA_GIS_Geometry
return $wkt;
}
/**
* Calculates the area of a closed simple polygon.
*
* @param array $ring array of points forming the ring
*
* @return the area of a closed simple polygon.
*/
public static function area($ring) {
$no_of_points = count($ring);
// If the last point is same as the first point ignore it
$last = count($ring) - 1;
if (($ring[0]['x'] == $ring[$last]['x']) && ($ring[0]['y'] == $ring[$last]['y'])) {
$no_of_points--;
}
// _n-1
// A = _1_ \ (X(i) * Y(i+1)) - (Y(i) * X(i+1))
// 2 /__
// i=0
$area = 0;
for ($i = 0; $i < $no_of_points; $i++) {
$j = ($i + 1) % $no_of_points;
$area += $ring[$i]['x'] * $ring[$j]['y'];
$area -= $ring[$i]['y'] * $ring[$j]['x'];
}
$area /= 2.0;
return $area;
}
/**
* Determines whether a set of points represents an outer ring.
* If points are in clockwise orientation then, they form an outer ring.
*
* @param array $ring array of points forming the ring
*
* @return whether a set of points represents an outer ring.
*/
public static function isOuterRing($ring)
{
// If area is negative then it's in clockwise orientation, i.e. it's an outer ring
if (PMA_GIS_Polygon::area($ring) < 0) {
return true;
}
return false;
}
/**
* Determines whether a given point is inside a given polygon.
*
* @param array $point x, y coordinates of the point
* @param array $ring array of points forming the ring
*
* @return whether a given point is inside a given polygon
*/
public static function isPointInsidePolygon($point, $polygon)
{
// If first point is repeated at the end remove it
$last = count($polygon) - 1;
if (($polygon[0]['x'] == $polygon[$last]['x']) && ($polygon[0]['y'] == $polygon[$last]['y'])) {
$polygon = array_slice($polygon, 0, $last);
}
$no_of_points = count($polygon);
$counter = 0;
// Use ray casting algorithm
$p1 = $polygon[0];
for ($i = 1; $i <= $no_of_points; $i++) {
$p2 = $polygon[$i % $no_of_points];
if ($point['y'] > min(array($p1['y'], $p2['y']))) {
if ($point['y'] <= max(array($p1['y'], $p2['y']))) {
if ($point['x'] <= max(array($p1['x'], $p2['x']))) {
if ($p1['y'] != $p2['y']) {
$xinters = ($point['y'] - $p1['y']) * ($p2['x'] - $p1['x']) / ($p2['y'] - $p1['y']) + $p1['x'];
if ($p1['x'] == $p2['x'] || $point['x'] <= $xinters) {
$counter++;
}
}
}
}
}
$p1 = $p2;
}
if ($counter % 2 == 0) {
return false;
} else {
return true;
}
}
/** Generate parameters for the GIS data editor from the value of the GIS column.
*
* @param string $value of the GIS column

View File

@ -18,8 +18,11 @@ if (isset($plugin_list)) {
);
} else {
ini_set('memory_limit', '128M');
set_time_limit(120);
if ((int) ini_get('memory_limit') < 512) {
ini_set('memory_limit', '512M');
}
set_time_limit(300);
// Append the bfShapeFiles directory to the include path variable
set_include_path(get_include_path() . PATH_SEPARATOR . getcwd() . '/libraries/bfShapeFiles/');
@ -208,7 +211,7 @@ if (isset($plugin_list)) {
}
$shp = new PMA_ShapeFile(1);
// If the zip archive has more than one file, get the content to the buffer from .shp file.
// If the zip archive has more than one file, get the correct content to the buffer from .shp file.
if ($compression == 'application/zip' && PMA_getNoOfFilesInZip($import_file) > 1) {
$zip_content = PMA_getZipContents($import_file, '/^.*\.shp$/i');
$GLOBALS['import_text'] = $zip_content['data'];
@ -227,7 +230,7 @@ if (isset($plugin_list)) {
// Extract the .dbf file and point to it.
$extracted = PMA_zipExtract($import_file, realpath($cfg['TempDir']), array($dbf_file_name));
if ($extracted) {
$dbf_file_path = realpath($cfg['TempDir']) . '/' . $dbf_file_name;
$dbf_file_path = realpath($cfg['TempDir']) . (PMA_IS_WINDOWS ? '\\' : '/') . $dbf_file_name;
$temp_dbf_file = true;
// Replace the .dbf with .*, as required by the bsShapeFiles library.
$file_name = substr($dbf_file_path, 0, strlen($dbf_file_path) - 4) . '.*';