public function GetLayerKml($resId, $format = "kml") { //Check for unsupported representations $fmt = $this->ValidateRepresentation($format, array("kml", "kmz")); $width = $this->GetRequestParameter("width", null); $height = $this->GetRequestParameter("height", null); $drawOrder = $this->GetRequestParameter("draworder", null); $dpi = $this->GetRequestParameter("dpi", 96); $bbox = $this->GetRequestParameter("bbox", null); $extents = null; if ($width == null) { $this->BadRequest($this->app->localizer->getText("E_MISSING_REQUIRED_PARAMETER", "width"), $this->GetMimeTypeForFormat($format)); } if ($height == null) { $this->BadRequest($this->app->localizer->getText("E_MISSING_REQUIRED_PARAMETER", "height"), $this->GetMimeTypeForFormat($format)); } if ($drawOrder == null) { $this->BadRequest($this->app->localizer->getText("E_MISSING_REQUIRED_PARAMETER", "draworder"), $this->GetMimeTypeForFormat($format)); } if ($bbox == null) { $this->BadRequest($this->app->localizer->getText("E_MISSING_REQUIRED_PARAMETER", "bbox"), $this->GetMimeTypeForFormat($format)); } else { $parts = explode(",", $bbox); if (count($parts) == 4) { $extents = new MgEnvelope($parts[0], $parts[1], $parts[2], $parts[3]); } } $sessionId = ""; if ($resId->GetRepositoryType() == MgRepositoryType::Session) { $sessionId = $resId->GetRepositoryName(); } else { $sessionId = $this->GetRequestParameter("session", ""); } $this->EnsureAuthenticationForSite($sessionId, true); $siteConn = new MgSiteConnection(); if ($sessionId !== "") { $userInfo = new MgUserInformation($sessionId); $siteConn->Open($userInfo); } else { $siteConn->Open($this->userInfo); $site = $siteConn->GetSite(); $sessionId = $site->CreateSession(); $userInfo = new MgUserInformation($sessionId); $siteConn->Open($userInfo); } $csFactory = new MgCoordinateSystemFactory(); $csObj = $csFactory->CreateFromCode("LL84"); $scale = MgUtils::GetScale($extents, $csObj, $width, $height, $dpi); $writer = new MgSlimChunkWriter($this->app); $doc = new MgKmlDocument($writer); $resSvc = $siteConn->CreateService(MgServiceType::ResourceService); $featSvc = $siteConn->CreateService(MgServiceType::FeatureService); $ldfContent = $resSvc->GetResourceContent($resId); $xml = new DOMDocument(); $xml->loadXML($ldfContent->ToString()); $destExtent = self::GetLayerExtent($csFactory, $xml, $csObj, $featSvc, $resSvc); $doc->StartDocument(); $doc->WriteString("<visibility>1</visibility>"); if ($destExtent != null) { $widthMeters = $csObj->ConvertCoordinateSystemUnitsToMeters($destExtent->GetWidth()); $heightMeters = $csObj->ConvertCoordinateSystemUnitsToMeters($destExtent->GetHeight()); $dimension = sqrt($widthMeters * $heightMeters); $vlNodes = $xml->getElementsByTagName("VectorLayerDefinition"); $glNodes = $xml->getElementsByTagName("GridLayerDefinition"); $dlNodes = $xml->getElementsByTagName("DrawingLayerDefinition"); if ($vlNodes->length == 1) { $scaleRangeNodes = $vlNodes->item(0)->getElementsByTagName("VectorScaleRange"); for ($i = 0; $i < $scaleRangeNodes->length; $i++) { $scaleRange = $scaleRangeNodes->item($i); $minElt = $scaleRange->getElementsByTagName('MinScale'); $maxElt = $scaleRange->getElementsByTagName('MaxScale'); $minScale = "0"; $maxScale = 'infinity'; // as MDF's VectorScaleRange::MAX_MAP_SCALE if ($minElt->length > 0) { $minScale = $minElt->item(0)->nodeValue; } if ($maxElt->length > 0) { $maxScale = $maxElt->item(0)->nodeValue; } if ($minScale != 'infinity') { $minScale = intval($minScale); } if ($maxScale != 'infinity') { $maxScale = intval($maxScale); } else { $maxScale = 1000000000000.0; } // as MDF's VectorScaleRange::MAX_MAP_SCALE if ($scale > $minScale && $scale <= $maxScale) { $this->AppendScaleRange($resId, $destExtent, $dimension, $minScale, $maxScale, $dpi, $drawOrder, $format, $sessionId, $doc); } } } else { if ($glNodes->length == 1) { } } } $doc->EndDocument(); }
public function GetTileXYZ($resId, $groupName, $x, $y, $z, $type, $layerNames = NULL) { $fmt = $this->ValidateRepresentation($type, array("json", "png", "png8", "jpg", "gif")); $path = self::GetTilePath($this->app, $resId, $groupName, $z, $x, $y, $type, $layerNames); clearstatcache(); $dir = dirname($path); $lockPath = "{$dir}/lock_" . $y . ".lck"; $attempts = 0; while (!@is_dir($dir)) { try { mkdir($dir, 0777, true); } catch (Exception $e) { //Another tile request may have already created this since $attempts++; //Bail after MAX_RETRY_ATTEMPTS if ($attempts >= self::MAX_RETRY_ATTEMPTS) { $this->ServerError($this->app->localizer->getText("E_FAILED_TO_CREATE_DIR_AFTER_N_ATTEMPTS", $attempts), $this->GetMimeTypeForFormat($type)); } } } //If there's a dangling lock file, attempt to remove it if (file_exists($lockPath)) { unlink($lockPath); } $fpLockFile = fopen($lockPath, "a+"); //Message of any exception caught will be set to this variable $tileError = null; $requestId = rand(); $this->app->log->debug("({$requestId}) Checking if {$path} exists"); $attempts = 0; while (!file_exists($path)) { //Bail after MAX_RETRY_ATTEMPTS if ($attempts >= self::MAX_RETRY_ATTEMPTS) { $this->ServerError($this->app->localizer->getText("E_FAILED_TO_GENERATE_TILE_AFTER_N_ATTEMPTS", $attempts), $this->GetMimeTypeForFormat($type)); } $attempts++; $this->app->log->debug("({$requestId}) {$path} does not exist. Locking for writing"); $bLocked = false; flock($fpLockFile, LOCK_EX); fwrite($fpLockFile, "."); $bLocked = true; $this->app->log->debug("({$requestId}) Acquired lock for {$path}. Checking if path exists again."); //check once more to see if the cache file was created while waiting for //the lock clearstatcache(); if (!file_exists($path)) { try { $this->app->log->debug("({$requestId}) Rendering tile to {$path}"); $bOldPath = true; if ($type != "json") { //if this is MGOS 3.0 and we're dealing with a tile set, we invoke GETTILEIMAGE as that we can pass in Tile Set Definition //resource ids without issues. We cannot create MgMaps from Tile Set Definitions that are not using the default tile provider. // //The given tile set is assumed to be using the XYZ provider, the case where the Tile Set Definition is using the default provider //is not handled if ($this->app->MG_VERSION[0] >= 3 && $resId->GetResourceType() == "TileSetDefinition") { $bOldPath = false; $sessionId = ""; if ($resId->GetRepositoryType() === MgRepositoryType::Session && $this->app->request->get("session") == null) { $sessionId = $resId->GetRepositoryName(); } $resIdStr = $resId->ToString(); $that = $this; $this->EnsureAuthenticationForHttp(function ($req, $param) use($that, $resIdStr, $groupName, $x, $y, $z, $requestId, $path) { $param->AddParameter("OPERATION", "GETTILEIMAGE"); $param->AddParameter("VERSION", "1.2.0"); $param->AddParameter("MAPDEFINITION", $resIdStr); $param->AddParameter("BASEMAPLAYERGROUPNAME", $groupName); $param->AddParameter("SCALEINDEX", $z); $param->AddParameter("TILEROW", $x); $param->AddParameter("TILECOL", $y); $that->app->log->debug("({$requestId}) Executing GETTILEIMAGE"); $that->ExecuteHttpRequest($req, function ($result, $status) use($path) { if ($status == 200) { //Need to dump the rendered tile to the specified path so the caching stuff below can still do its thing $resultObj = $result->GetResultObject(); $sink = new MgByteSink($resultObj); $sink->ToFile($path); } }); }, true, "", $sessionId); //Tile access can be anonymous, so allow for it if credentials/session specified, but if this is a session-based Map Definition, use the session id as the nominated one } } //Pre MGOS 3.0 code path if ($bOldPath) { $this->app->log->debug("({$requestId}) Going down old code path"); $this->EnsureAuthenticationForSite("", true); $siteConn = new MgSiteConnection(); $siteConn->Open($this->userInfo); $map = new MgMap($siteConn); $map->Create($resId, "VectorTileMap"); $renderSvc = $siteConn->CreateService(MgServiceType::RenderingService); $groups = $map->GetLayerGroups(); $baseGroup = $groups->GetItem($groupName); //Will throw MgObjectNotFoundException -> 404 if no such group exists $factory = new MgCoordinateSystemFactory(); $mapCsWkt = $map->GetMapSRS(); $mapCs = $factory->Create($mapCsWkt); $mapExtent = $map->GetMapExtent(); $mapExLL = $mapExtent->GetLowerLeftCoordinate(); $mapExUR = $mapExtent->GetUpperRightCoordinate(); $metersPerUnit = $mapCs->ConvertCoordinateSystemUnitsToMeters(1.0); $this->app->log->debug("({$requestId}) Calc bounds from XYZ"); //XYZ to lat/lon math. From this we can convert to the bounds in the map's CS // //Source: http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames $n = pow(2, $z); $lonMin = $x / $n * 360.0 - 180.0; $latMin = rad2deg(atan(sinh(pi() * (1 - 2 * $y / $n)))); $lonMax = ($x + 1) / $n * 360.0 - 180.0; $latMax = rad2deg(atan(sinh(pi() * (1 - 2 * ($y + 1) / $n)))); $boundsMinX = min($lonMin, $lonMax); $boundsMinY = min($latMin, $latMax); $boundsMaxX = max($lonMax, $lonMin); $boundsMaxY = max($latMax, $latMin); if ($mapCs->GetCsCode() != "LL84") { $llCs = $factory->CreateFromCode("LL84"); $trans = $factory->GetTransform($llCs, $mapCs); $ul = $trans->Transform($lonMin, $latMin); $lr = $trans->Transform($lonMax, $latMax); $boundsMinX = min($lr->GetX(), $ul->GetX()); $boundsMinY = min($lr->GetY(), $ul->GetY()); $boundsMaxX = max($lr->GetX(), $ul->GetX()); $boundsMaxY = max($lr->GetY(), $ul->GetY()); } //Set all layers under group to be visible $layers = $map->GetLayers(); $layerCount = $layers->GetCount(); $groupCount = $groups->GetCount(); //Turn all groups that are not the given group to be hidden for ($i = 0; $i < $groupCount; $i++) { $group = $groups->GetItem($i); if ($group->GetName() != $groupName) { $group->SetVisible(false); } else { $group->SetVisible(true); } } for ($i = 0; $i < $layerCount; $i++) { $layer = $layers->GetItem($i); $group = $layer->GetGroup(); if (null == $group) { continue; } if ($group->GetName() != $groupName && $layer->GetLayerType() == MgLayerType::Dynamic) { $layer->SetVisible(false); continue; } if ($layer->GetLayerType() == MgLayerType::Dynamic) { $layer->SetVisible(true); } } if ($type == "json") { //error_log("($requestId) Render vector tile"); $this->PutVectorTileXYZ($map, $groupName, $siteConn, $metersPerUnit, $factory, $path, $boundsMinX, $boundsMinY, $boundsMaxX, $boundsMaxY, $layerNames); } else { $format = strtoupper($type); //error_log("($requestId) Render image tile"); $this->PutTileImageXYZ($map, $groupName, $renderSvc, $path, $format, $boundsMinX, $boundsMinY, $boundsMaxX, $boundsMaxY, $layerNames, $requestId); } } } catch (MgException $ex) { if ($bLocked) { $this->app->log->debug("({$requestId}) MgException caught " . $ex->GetDetails() . "\n" . $ex->getTraceAsString() . "\n. Releasing lock for {$path}"); $tileError = $ex->GetExceptionMessage(); flock($fpLockFile, LOCK_UN); $bLocked = false; } if ($ex instanceof MgResourceNotFoundException || $ex instanceof MgObjectNotFoundException) { $this->NotFound($ex->GetExceptionMessage(), $this->GetMimeTypeForFormat($fmt)); } else { if ($ex instanceof MgConnectionFailedException) { $this->ServiceUnavailable($ex->GetExceptionMessage(), $this->GetMimeTypeForFormat($fmt)); } } } catch (Exception $ex) { if ($bLocked) { $tileError = get_class($ex) . " - " . $ex->getMessage(); $this->app->log->debug("({$requestId}) Exception caught ({$tileError}). Releasing lock for {$path}"); flock($fpLockFile, LOCK_UN); $bLocked = false; } } } if ($bLocked) { $this->app->log->debug("({$requestId}) Releasing lock for {$path}"); flock($fpLockFile, LOCK_UN); $bLocked = false; } } //An exception occurred, try to clean up lock before bailing if ($tileError != null) { try { fclose($fpLockFile); unlink($lockPath); } catch (Exception $ex) { $this->app->log->debug("({$requestId}) Failed to delete lock file. Perhaps another concurrent request to the same tile is happening?"); } throw new Exception($tileError); } $modTime = filemtime($path); $this->app->lastModified($modTime); $this->app->log->debug("({$requestId}) Acquiring shared lock for {$path}"); //acquire shared lock for reading to prevent a problem that could occur //if a tile exists but is only partially generated. flock($fpLockFile, LOCK_SH); $this->app->log->debug("({$requestId}) Outputting {$path}"); $ext = strtoupper(pathinfo($path, PATHINFO_EXTENSION)); $mimeType = ""; switch ($ext) { case "PNG": //MgImageFormats::Png: $mimeType = MgMimeType::Png; break; case "GIF": //MgImageFormats::Gif: $mimeType = MgMimeType::Gif; break; case "JPG": //MgImageFormats::Jpeg: $mimeType = MgMimeType::Jpeg; break; case "JSON": $mimeType = MgMimeType::Json; break; } $this->app->response->header("Content-Type", $mimeType); $this->app->expires("+6 months"); $this->app->response->header("Cache-Control", "max-age=31536000, must-revalidate"); $this->app->response->setBody(file_get_contents($path)); $this->app->log->debug("({$requestId}) Releasing shared lock for {$path}"); //Release lock flock($fpLockFile, LOCK_UN); //Try to delete the lock file try { fclose($fpLockFile); unlink($lockPath); } catch (Exception $ex) { $this->app->log->debug("({$requestId}) Failed to delete lock file. Perhaps another concurrent request to the same tile is happening?"); } }
public static function GetFeatureClassMBR($app, $featureSrvc, $featuresId, $schemaName, $className, $geomName = null, $transformToCsCode = null) { $extentGeometryAgg = null; $extentGeometrySc = null; $extentByteReader = null; $mbr = new stdClass(); $csFactory = new MgCoordinateSystemFactory(); $clsDef = $featureSrvc->GetClassDefinition($featuresId, $schemaName, $className); $props = $clsDef->GetProperties(); if ($geomName == null) { $geomName = $clsDef->GetDefaultGeometryPropertyName(); } $geomProp = $props->GetItem($geomName); if ($geomProp->GetPropertyType() != MgFeaturePropertyType::GeometricProperty) { throw new Exception($app->localizer->getText("E_NOT_GEOMETRY_PROPERTY", $geomName)); } $spatialContext = $geomProp->GetSpatialContextAssociation(); // Finds the coordinate system $agfReaderWriter = new MgAgfReaderWriter(); $spatialcontextReader = $featureSrvc->GetSpatialContexts($featuresId, false); while ($spatialcontextReader->ReadNext()) { if ($spatialcontextReader->GetName() == $spatialContext) { $mbr->coordinateSystem = $spatialcontextReader->GetCoordinateSystemWkt(); $mbr->csCode = $csFactory->ConvertWktToCoordinateSystemCode($mbr->coordinateSystem); $mbr->epsg = $csFactory->ConvertWktToEpsgCode($mbr->coordinateSystem); // Finds the extent $extentByteReader = $spatialcontextReader->GetExtent(); break; } } $spatialcontextReader->Close(); if ($extentByteReader != null) { // Get the extent geometry from the spatial context $extentGeometrySc = $agfReaderWriter->Read($extentByteReader); } // Try to get the extents using the selectaggregate as sometimes the spatial context // information is not set $aggregateOptions = new MgFeatureAggregateOptions(); $featureProp = 'SPATIALEXTENTS("' . $geomName . '")'; $aggregateOptions->AddComputedProperty('EXTENTS', $featureProp); try { $dataReader = $featureSrvc->SelectAggregate($featuresId, $className, $aggregateOptions); if ($dataReader->ReadNext()) { // Get the extents information $byteReader = $dataReader->GetGeometry('EXTENTS'); $extentGeometryAgg = $agfReaderWriter->Read($byteReader); } $dataReader->Close(); } catch (MgException $e) { if ($extentGeometryAgg == null) { //We do have one last hope. EXTENT() is an internal MapGuide custom function that's universally supported //as it operates against an underlying select query result. This raw-spins the reader server-side so there //is no server -> web tier transmission overhead involved. try { $aggregateOptions = new MgFeatureAggregateOptions(); $aggregateOptions->AddComputedProperty("COMP_EXTENT", "EXTENT(" . $geomName . ")"); $dataReader = $featureSrvc->SelectAggregate($featuresId, $className, $aggregateOptions); if ($dataReader->ReadNext()) { // Get the extents information $byteReader = $dataReader->GetGeometry('COMP_EXTENT'); $extentGeometryAgg = $agfReaderWriter->Read($byteReader); } $dataReader->Close(); } catch (MgException $e2) { } } } $mbr->extentGeometry = null; // Prefer SpatialExtents() of EXTENT() result over spatial context extent if ($extentGeometryAgg != null) { $mbr->extentGeometry = $extentGeometryAgg; } if ($mbr->extentGeometry == null) { //Stil null? Now try spatial context if ($extentGeometrySc != null) { $mbr->extentGeometry = $extentGeometrySc; } } if ($transformToCsCode != null) { $sourceCs = $csFactory->CreateFromCode($mbr->csCode); $targetCs = $csFactory->CreateFromCode($transformToCsCode); $xform = $csFactory->GetTransform($sourceCs, $targetCs); $mbr->extentGeometry = $mbr->extentGeometry->Transform($xform); $mbr->csCode = $targetCs->GetCsCode(); $mbr->epsg = $targetCs->GetEpsgCode(); } return $mbr; }
public function TransformCoordinates() { $source = $this->app->request->post("from"); $target = $this->app->request->post("to"); $coordList = $this->app->request->post("coords"); $format = $this->app->request->post("format"); if ($format == null) { $format = "xml"; } $fmt = $this->ValidateRepresentation($format, array("xml", "json")); if ($source == null) { $this->BadRequest($this->app->localizer->getText("E_MISSING_REQUIRED_PARAMETER", "from"), $this->GetMimeTypeForFormat($format)); } if ($target == null) { $this->BadRequest($this->app->localizer->getText("E_MISSING_REQUIRED_PARAMETER", "to"), $this->GetMimeTypeForFormat($format)); } if ($coordList == null) { $this->BadRequest($this->app->localizer->getText("E_MISSING_REQUIRED_PARAMETER", "coords"), $this->GetMimeTypeForFormat($format)); } try { $factory = new MgCoordinateSystemFactory(); $sourceCs = $factory->CreateFromCode($source); $targetCs = $factory->CreateFromCode($target); $trans = $factory->GetTransform($sourceCs, $targetCs); $coords = explode(",", $coordList); $output = "<?xml version=\"1.0\" encoding=\"utf-8\"?><CoordinateCollection>"; foreach ($coords as $coordPair) { $tokens = explode(" ", trim($coordPair)); $tokenCount = count($tokens); if ($tokenCount === 2) { $txCoord = $trans->Transform(floatval($tokens[0]), floatval($tokens[1])); $output .= "<Coordinate><X>" . $txCoord->GetX() . "</X><Y>" . $txCoord->GetY() . "</Y></Coordinate>"; } else { //TODO: We should accept a partial response, but there's currently no way an empty <Coordinate/> tag survives the //XML to JSON conversion, so we have to throw lest we return an inconsisten partial result $this->ServerError($this->app->localizer->getText("E_INVALID_COORDINATE_PAIR", $coordPair, $tokenCount), $this->GetMimeTypeForFormat($format)); } } $output .= "</CoordinateCollection>"; if ($fmt === "json") { $this->app->response->header("Content-Type", MgMimeType::Json); $json = MgUtils::Xml2Json($output); $this->app->response->write($json); } else { $this->app->response->header("Content-Type", MgMimeType::Xml); $this->app->response->write($output); } } catch (MgException $ex) { $this->OnException($ex, $this->GetMimeTypeForFormat($format)); } }