<?php

// --------- Raster Config Builder ---------------
// 
// This script allows you to build a config file
// for a directory full of images and take advantage
// of the image catalog support in the GDAL FDO provider
//
// Version 1.0
//
// -----------------------------------------------

// user-specified constants

define ("USERNAME""Administrator");
define ("PASSWORD""admin");
define ("INSTALLDIR""C:/Program Files/MapGuideOpenSource/");

// The value of this constant is a regular expression which specifies
// which file extensions are valid images.  This improves performance
// slightly in directories with lots of rrd, aux, tfw, tab, etc files.
// If you need an additional file type, add it to this list.

define ("IMAGEEXT""/(png)|(jpg)|(tif)|(tiff)|(ecw)|(sid)|(gif)/");

// -----------------------------------------------
//
// Shouldn't have to change anything below this line.
// If you're optimistic...
//
// -----------------------------------------------

// change the timeout for large data sets...

ini_set("max_execution_time","300");
ini_set('error_reporting'E_ALL);

// MapGuide needs this info
define ("CONFIGFILE"INSTALLDIR "WebServerExtensions/www/webconfig.ini");

// GDAL FeatureSource content template:  filepath, config document entity
define ("TEMPLATE_FS"'<?xml version="1.0" encoding="utf-8"?><FeatureSource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:noNamespaceSchemaLocation="FeatureSource-1.0.0.xsd"><Provider>OSGeo.Gdal</Provider><Parameter><Name>DefaultRasterFileLocation</Name><Value>%s</Value></Parameter>%s</FeatureSource>');

// Config file Feature template:  filename, filename, MinX, MinY, MaxX, MaxY
define ("TEMPLATE_FEAT"'<Feature name="%s"><Band name="RGB" number="1"><Image frame="1" name="%s"><Bounds><MinX>%s</MinX><MinY>%s</MinY><MaxX>%s</MaxX><MaxY>%s</MaxY></Bounds></Image></Band></Feature>');

// Config file SchemaMapping template:  imagepath, feature list
define ("TEMPLATE_SMAP"'<SchemaMapping provider="OSGeo.Gdal.3.2" name="default" xmlns="http://fdogrfp.osgeo.org/schemas"><complexType name="defaultType"><complexType name="RasterTypeType"><RasterDefinition name="images"><Location name="%s">%s</Location></RasterDefinition></complexType></complexType></SchemaMapping>');

// This should really come from GetSchemaMapping, but it's broken:  minX, minY, maxX, maxY
define ("TEMPLATE_CFG"'<?xml version="1.0" encoding="UTF-8"?><fdo:DataStore xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:gml="http://www.opengis.net/gml" xmlns:fdo="http://fdo.osgeo.org/schemas" xmlns:fds="http://fdo.osgeo.org/schemas/fds"><gml:DerivedCRS gml:id="Default"><gml:metaDataProperty><gml:GenericMetaData><fdo:SCExtentType>dynamic</fdo:SCExtentType><fdo:XYTolerance>0.001000</fdo:XYTolerance><fdo:ZTolerance>0.001000</fdo:ZTolerance></gml:GenericMetaData></gml:metaDataProperty><gml:remarks>System generated default FDO Spatial Context</gml:remarks><gml:srsName>Default</gml:srsName><gml:validArea><gml:boundingBox><gml:pos>%s %s</gml:pos><gml:pos>%s %s</gml:pos></gml:boundingBox></gml:validArea><gml:baseCRS><fdo:WKTCRS gml:id="Default"><gml:srsName>Default</gml:srsName><fdo:WKT>LOCAL_CS["*XY-MT*",LOCAL_DATUM["*X-Y*",10000],UNIT["Meter", 1],AXIS["X",EAST],AXIS["Y",NORTH]]</fdo:WKT></fdo:WKTCRS></gml:baseCRS><gml:definedByConversion xlink:href="http://fdo.osgeo.org/coord_conversions#identity"/><gml:derivedCRSType codeSpace="http://fdo.osgeo.org/crs_types">geographic</gml:derivedCRSType><gml:usesCS xlink:href="http://fdo.osgeo.org/cs#default_cartesian"/></gml:DerivedCRS><xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fdo="http://fdo.osgeo.org/schemas" xmlns:gml="http://www.opengis.net/gml" xmlns:default="http://fdo.osgeo.org/schemas/feature/default" targetNamespace="http://fdo.osgeo.org/schemas/feature/default" elementFormDefault="qualified" attributeFormDefault="unqualified"><xs:annotation><xs:appinfo source="http://fdo.osgeo.org/schemas"/></xs:annotation><xs:element name="default" type="default:defaultType" abstract="false" substitutionGroup="gml:_Feature"><xs:key name="defaultKey"><xs:selector xpath=".//default"/><xs:field xpath="FeatId"/></xs:key></xs:element><xs:complexType name="defaultType" abstract="false" fdo:hasGeometry="false"><xs:annotation><xs:appinfo source="http://fdo.osgeo.org/schemas"/></xs:annotation><xs:complexContent><xs:extension base="gml:AbstractFeatureType"><xs:sequence><xs:element name="FeatId"><xs:annotation><xs:appinfo source="http://fdo.osgeo.org/schemas"/></xs:annotation><xs:simpleType><xs:restriction base="xs:string"><xs:maxLength value="256"/></xs:restriction></xs:simpleType></xs:element><xs:element name="Image" type="fdo:RasterPropertyType" fdo:defaultImageXSize="1024" fdo:defaultImageYSize="1024" fdo:srsName="Default"><xs:annotation><xs:appinfo source="http://fdo.osgeo.org/schemas"><fdo:DefaultDataModel dataModelType="Bitonal" dataType="Unknown" organization="Pixel" bitsPerPixel="1" tileSizeX="256" tileSizeY="256"/></xs:appinfo></xs:annotation></xs:element></xs:sequence></xs:extension></xs:complexContent></xs:complexType></xs:schema><SchemaMapping xmlns="http://fdogrfp.osgeo.org/schemas" provider="OSGeo.Gdal.3.2" name="default"></SchemaMapping></fdo:DataStore>');


// This is only required because we want to avoid a dependancy on constants.php for ASPX installs:

class MgServiceType
{
   
////////////////////////////////////////////////////////////////
   /// Resource Service
   
const ResourceService 
   
////////////////////////////////////////////////////////////////
   /// DWF Drawing Service
   
const DrawingService 
   
////////////////////////////////////////////////////////////////
   /// FDO Feature Service
   
const FeatureService 
   
////////////////////////////////////////////////////////////////
   /// Mapping Service
   
const MappingService 
   
////////////////////////////////////////////////////////////////
   /// Rendering Service
   
const RenderingService 
   
////////////////////////////////////////////////////////////////
   /// Tile Service
   
const TileService 
   
////////////////////////////////////////////////////////////////
   /// Kml Service
   
const KmlService 
   
}


// This function checks to make sure that the full path with directory separator exists,
// returns the full path if so, or "" if not.
function validPath($path)
{

  if (
is_dir($path))
  {
    return 
realpath($path) . DIRECTORY_SEPARATOR;
  }
  return 
"";

}


// This function does some DOM manipulation to swap the schema mapping element into the config XML
function replaceSchema($originalXML$replacementXML$swapTag)
{

  
// convert the strings into DOM objects

  
$originalDOM = new DOMDocument;
  
$originalDOM->loadXML($originalXML);
  
  
$replacementDOM = new DOMDocument;
  
$replacementDOM->loadXML($replacementXML);
  
  
// add the new node to the config document
  
$newNode $originalDOM->importNode($replacementDOM->documentElementtrue);
  
  
// locate the old node to swap out
  
$oldNode $originalDOM->getElementsByTagName($swapTag)->item(0);
  
  
// and replace the old node with the new node
  
$oldNode->parentNode->replaceChild($newNode$oldNode);
  
  return 
$originalDOM->saveXML();

}

// This function does the heavy lifting.  Loads each of the images into MapGuide,
// pulls its extents, then appends that to a string.  Finally, swaps this big
// string into the output of GetSchemaMapping to generate a config document
function createResource ($imagePath,$resourcePath)
{

  try 
  {

    if (!
$dh opendir($imagePath)) 
    {
      return ;
    }
    
    
// we've got a valid path that can be opened, so let's set up some MapGuide stuff...

    
MgInitializeWebTier(CONFIGFILE);

    
$user = new MgUserInformation(USERNAMEPASSWORD);
    
$siteConnection = new MgSiteConnection();
    
$siteConnection->Open($user);

    
$site $siteConnection->GetSite();
    
$sessionID $site->CreateSession();
    
$user->SetMgSessionId($sessionID);

    
$resourceService $siteConnection->CreateService(MgServiceType::ResourceService);
    
$featureService $siteConnection->CreateService(MgServiceType::FeatureService);

    
$agfReaderWriter = new MgAgfReaderWriter();

    
// and now, let's generate a set of <Bounds> elements for the config file, 
    // from all of the images in the passed-in directory

    
$features "";

    
$minX 999999999999;
    
$minY 999999999999;
    
$maxX = -999999999999;
    
$maxY = -999999999999;
 
    while ((
$file readdir($dh)) !== false
    {
 
      
$pathInfo pathinfo($imagePath.$file);
      
      if ( 
is_file $imagePath.$file ) &&  preg_match(IMAGEEXT,$pathInfo['extension']) > )
      {

        
// set up a unique identifier for a temporary FeatureSource
        
$fsIdentifier 'Session:' $sessionID '//' md5($imagePath.$file) . '.FeatureSource';
        
$tmpResource = new MgResourceIdentifier($fsIdentifier);
        
        
// create a new feature source definition from the GDAL FeatureSource template
        
$fsDefinition sprintf(TEMPLATE_FS,$imagePath.$file,'');
        
$tmpByteSource = new MgByteSource($fsDefinitionstrlen($fsDefinition));
                                                                                   
        
// write the temporary FeatureSource to the repository
        
$resourceService->SetResource($tmpResource$tmpByteSource->GetReader(), null);
        
        
// grab the spatial contexts
        
$tmpSpatialContextReader $featureService->GetSpatialContexts($tmpResourcefalse);
        
        
// advance to the first (hopefully only...) record
        
$tmpSpatialContextReader->ReadNext();
        
        
// pull out the bounding box and append to the feature list
        
$scExtentByteReader $tmpSpatialContextReader->GetExtent();
        if ( 
$scExtentByteReader != NULL 
        {

          
$extentGeom $agfReaderWriter->Read($scExtentByteReader);
          
          
$scMinX $extentGeom->Envelope()->GetLowerLeftCoordinate()->GetX();
          
$scMinY $extentGeom->Envelope()->GetLowerLeftCoordinate()->GetY();
          
$scMaxX $extentGeom->Envelope()->GetUpperRightCoordinate()->GetX();
          
$scMaxY $extentGeom->Envelope()->GetUpperRightCoordinate()->GetY();
          
          
$minX min ($minX$scMinX); 
          
$minY min ($minY$scMinY); 
          
$maxX max ($maxX$scMaxX); 
          
$maxY max ($maxY$scMaxY); 
          
          
$features .= sprintf(TEMPLATE_FEAT$file$file$scMinX$scMinY$scMaxX$scMaxY);
        }

        
// close the reader
        
$tmpSpatialContextReader->Close();

        
// delete the temporary resource
        
$resourceService->DeleteResource($tmpResource);

      }

    }
    
    
closedir($dh);
    
    if (empty(
$features))
    {
      
$site->DestroySession($sessionID);
      return 
"No images found.";
    }

    
// now, take the feature XML and insert it into a SchemaMapping template
    
$schemaMappingXML sprintf(TEMPLATE_SMAP,$imagePath,$features);
      
    
// TODO: re-enable this stuff when GetSchemaMapping bug is fixed 
    // retrieve the current Schema for a connection to the image path
    // $configXML = $featureService->GetSchemaMapping("OSGeo.Gdal.3.2","DefaultRasterFileLocation=$imagePath")->ToString();
    // fix for MapGuide hardcode that prevents spatial queries on features named "Raster"
    // $configXML = str_replace('<xs:element name="Raster"','<xs:element name="Image"',$configXML);

    // hack: replace the extents of the current image catalog into the generic schema template
    
$configXML sprintf(TEMPLATE_CFG,$minX,$minY,$maxX,$maxY);

    
// replace the SchemaMapping element with that generated above
    
$outputXML replaceSchema($configXML$schemaMappingXML'SchemaMapping');

    
// ensure that it's in UTF encoding
    
$outputUTF mb_convert_encoding($outputXML,"UTF-8","auto");

    
// turn the resourcePath into a resourceIdentifier
    
$resourceID = new MgResourceIdentifier($resourcePath);

    
// create XML for the new FeatureSource
    
$resourceDefinition sprintf(TEMPLATE_FS,$imagePath,'<ConfigurationDocument>ConfigurationDocument</ConfigurationDocument> ');

    
// read it into a byteSource
    
$contentByteSource = new MgByteSource($resourceDefinitionstrlen($resourceDefinition));
                                                                                   
    
// and write it to the repository
    
$resourceService->SetResource($resourceID$contentByteSource->GetReader(), null );

    
// if that didn't crash out, we should be OK setting the resource data...
    
    // create a byteSource for the resource DATA
    
$dataByteSource = new MgByteSource($outputUTFstrlen($outputUTF));

    
// upload it into the correct location in the repository
    
$resourceService->SetResourceData($resourceID"ConfigurationDocument" "Stream"$dataByteSource->GetReader()); 
    
    
$site->DestroySession($sessionID);

    return 
'Resource created sucessfully!';

  } 
  catch ( 
MgException $ex 
  {
    return 
$ex->GetDetails();
  } 

}

// prints the initial web form
function generateView($message="",$imagepath='C:\Path\To\My\Imagedir\\',$resourcepath='Library://Test/Data/MyImages.FeatureSource')
{

  
$output '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><title>Raster Config Builder</title></head><body>';

  
$output .= '<h1>Raster Config Builder</h1>';

  
$output .= '<div>Please enter the path to your image directory, location for new FeatureSource and click on Generate</div>';

  
$output .= '<form action="'.$_SERVER["PHP_SELF"].'" method="POST">';
  
$output .= '<label for="imagepath">Path to images on server: </label><br /><input id="imagepath" name="imagepath" size="100" maxlength="200"  value="'.$imagepath.'" /><br /><br />';
  
$output .= '<label for="resourcepath">Full path for new FeatureSource: </label><br /><input id="resourcepath" name="resourcepath" size="100" maxlength="200" value="'.$resourcepath.'" /><br /><br />';
  
$output .= '<input type="submit" name="action" value="Generate" />';

  if (!empty(
$message))
  {
    
$output .= '&nbsp;&nbsp;&nbsp;<span style="color:#ff0000;"><em>'.$message.'</em></span>';
  }

  
$output .= '</form>';

  
$output .= "</body></html>";

  
  return 
$output;

}

// Main handler

header('Content-type: text/html');
 
switch ( 
true )
{
  case (!isset(
$_POST["action"])):
    print( 
generateView() );
    break;
  case (!isset(
$_POST["imagepath"]) || !isset($_POST["resourcepath"])):
    print( 
generateView('You must specify an image path and destination FeatureSource') );
    break;
  case (
validPath($_POST["imagepath"]) === ""):
    print( 
generateView('Invalid image path',$_POST["imagepath"],$_POST["resourcepath"]) );
    break;
  default:
    print( 
generateViewcreateResourcevalidPath($_POST["imagepath"]) , $_POST["resourcepath"] ) , $_POST["imagepath"] , $_POST["resourcepath"] ) );
    break;
}

?>