diff --git a/libraries/gis/pma_gis_multipolygon.php b/libraries/gis/pma_gis_multipolygon.php index 96c144374c..31b9ce5729 100644 --- a/libraries/gis/pma_gis_multipolygon.php +++ b/libraries/gis/pma_gis_multipolygon.php @@ -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 diff --git a/libraries/gis/pma_gis_polygon.php b/libraries/gis/pma_gis_polygon.php index 239e219b65..f1c051ec40 100644 --- a/libraries/gis/pma_gis_polygon.php +++ b/libraries/gis/pma_gis_polygon.php @@ -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 diff --git a/libraries/import/shp.php b/libraries/import/shp.php index 4bfdf4d3db..abee9632f2 100644 --- a/libraries/import/shp.php +++ b/libraries/import/shp.php @@ -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) . '.*';