#region Disclaimer / License
// Copyright (C) 2009, Kenneth Skovhede
// http://www.hexad.dk, opensource@hexad.dk
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
#endregion
using System;
using System.Xml;
using System.Collections;
using System.Windows.Forms;
namespace OSGeo.MapGuide.Maestro.ResourceEditors.FeatureSourceEditors.Gdal
{
///
/// This class contains all code that manipulates the configuration document.
/// It will run in a seperate thread.
/// This is a .Net adaption of the PHP script:
/// http://www.jasonbirch.com/fdogdal/rasterconfig.phps
///
public class ConfigUpdater
{
//These 4 strings are from a script by Jason Birch:
//http://www.jasonbirch.com/fdogdal/rasterconfig.phps
// Config file Feature template: filename, filename, MinX, MinY, MaxX, MaxY
private const string TEMPLATE_FEAT = "{2}{3}{4}{5}";
//Config file location entry: imagepath, features
private const string TEMPLATE_LOC = "{1}";
// Config file SchemaMapping template: feature list
private const string TEMPLATE_SMAP = "{0}";
// This should really come from GetSchemaMapping, but it's broken: minX, minY, maxX, maxY
private const string TEMPLATE_CFG = "dynamic0.0010000.001000System generated default FDO Spatial ContextDefault{0} {1}{2} {3}" +
"DefaultLOCAL_CS[\"*XY-MT*\",LOCAL_DATUM[\"*X-Y*\",10000],UNIT[\"Meter\", 1],AXIS[\"X\",EAST],AXIS[\"Y\",NORTH]]geographic" +
"" +
"";
private string[] m_files;
private string[] m_remove;
private string[] m_add;
private OSGeo.MapGuide.MaestroAPI.FeatureSource m_feature;
private EditorInterface m_editor;
public ConfigUpdater(EditorInterface editor, OSGeo.MapGuide.MaestroAPI.FeatureSource feature)
{
m_editor = editor;
m_feature = feature;
}
public DialogResult UpdateItems(string[] newfiles, string[] toremove)
{
m_remove = toremove;
m_add = newfiles;
return m_editor.LengthyOperation(this, this.GetType().GetMethod("Runner", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance), false);
}
private string[] SplitPath(string input)
{
int ix = input.LastIndexOf("\\");
if (ix < 0)
ix = input.LastIndexOf("/");
string relname = input.Substring(ix + 1);
string folder = input.Substring(0, ix + 1);
return new string[] {folder, relname};
}
private bool Runner(string oldpath, string newpath, OSGeo.MapGuide.MaestroAPI.LengthyOperationCallBack callback, OSGeo.MapGuide.MaestroAPI.LengthyOperationProgressCallBack progress)
{
OSGeo.MapGuide.MaestroAPI.LengthyOperationCallbackArgs args = new OSGeo.MapGuide.MaestroAPI.LengthyOperationCallbackArgs(null);
Hashtable toplevels = new Hashtable();
ArrayList validitems = new ArrayList();
System.Text.StringBuilder sb = new System.Text.StringBuilder();
double minx, maxx, miny, maxy;
//Keep previous
Hashtable files = GetFilelist();
//Remove deleted
foreach(string s in m_remove)
{
if (files.ContainsKey(s))
files.Remove(s);
}
foreach(string s in m_add)
{
if (files.ContainsKey(s))
files.Remove(s);
}
foreach(string s in files.Keys)
{
string[] items = SplitPath(s);
if (!toplevels.ContainsKey(items[0]))
toplevels[items[0]] = new System.Text.StringBuilder();
((System.Text.StringBuilder)toplevels[items[0]]).Append(files[s]);
}
GetBounds(toplevels, out minx, out maxx, out miny, out maxy);
//Add new items
if (m_add.Length > 0)
{
OSGeo.MapGuide.MaestroAPI.LengthyOperationProgressArgs la = new OSGeo.MapGuide.MaestroAPI.LengthyOperationProgressArgs(Strings.ConfigUpdater.ProgressProcessingFiles, -1);
if (progress != null)
progress(this, la);
if (la.Cancel)
return false;
la.Progress = 100;
if (progress != null)
progress(this, la);
if (la.Cancel)
return false;
OSGeo.MapGuide.MaestroAPI.LengthyOperationCallbackArgs.LengthyOperationItem[] vi = new OSGeo.MapGuide.MaestroAPI.LengthyOperationCallbackArgs.LengthyOperationItem[m_add.Length];
for(int i = 0; i < vi.Length; i++)
vi[i] = new OSGeo.MapGuide.MaestroAPI.LengthyOperationCallbackArgs.LengthyOperationItem(m_add[i]);
args = new OSGeo.MapGuide.MaestroAPI.LengthyOperationCallbackArgs(vi);
if (callback != null)
callback(this, args);
if (args.Cancel)
return false;
if (args.Index > args.Items.Length)
return true;
if (args.Items.Length == 0)
return true;
do
{
OSGeo.MapGuide.MaestroAPI.LengthyOperationCallbackArgs.LengthyOperationItem item = args.Items[args.Index];
item.Status = OSGeo.MapGuide.MaestroAPI.LengthyOperationCallbackArgs.LengthyOperationItem.OperationStatus.Pending;
if (callback != null)
{
callback(this, args);
if (args.Cancel)
return false;
}
try
{
string tempname = new MaestroAPI.ResourceIdentifier(Guid.NewGuid().ToString(), OSGeo.MapGuide.MaestroAPI.ResourceTypes.FeatureSource, m_editor.CurrentConnection.SessionID);
OSGeo.MapGuide.MaestroAPI.FeatureSource fs = new OSGeo.MapGuide.MaestroAPI.FeatureSource();
fs.CurrentConnection = m_editor.CurrentConnection;
fs.Provider = m_feature.Provider;
fs.ConfigurationDocument = null;
fs.Parameter = new OSGeo.MapGuide.MaestroAPI.NameValuePairTypeCollection();
fs.Parameter["DefaultRasterFileLocation"] = item.Itempath;
try
{
m_editor.CurrentConnection.SaveResourceAs(fs, tempname);
OSGeo.MapGuide.MaestroAPI.FdoSpatialContextList lst = m_editor.CurrentConnection.GetSpatialContextInfo(tempname, false);
if (lst.SpatialContext != null && lst.SpatialContext.Count != 0 && lst.SpatialContext[0].Extent != null)
{
double local_minx = double.Parse(lst.SpatialContext[0].Extent.LowerLeftCoordinate.X, System.Globalization.CultureInfo.InvariantCulture);
double local_miny = double.Parse(lst.SpatialContext[0].Extent.LowerLeftCoordinate.Y, System.Globalization.CultureInfo.InvariantCulture);
double local_maxx = double.Parse(lst.SpatialContext[0].Extent.UpperRightCoordinate.X, System.Globalization.CultureInfo.InvariantCulture);
double local_maxy = double.Parse(lst.SpatialContext[0].Extent.UpperRightCoordinate.Y, System.Globalization.CultureInfo.InvariantCulture);
minx = Math.Min(minx, local_minx);
miny = Math.Min(miny, local_miny);
maxx = Math.Max(maxx, local_maxx);
maxy = Math.Max(maxy, local_maxy);
string[] items = SplitPath(item.Itempath);
if (!toplevels.ContainsKey(items[0]))
toplevels[items[0]] = new System.Text.StringBuilder();
((System.Text.StringBuilder)toplevels[items[0]]).Append(string.Format(System.Globalization.CultureInfo.InvariantCulture, TEMPLATE_FEAT, System.IO.Path.GetFileNameWithoutExtension(items[1]), items[1], local_minx, local_miny, local_maxx, local_maxy));
}
}
finally
{
try {m_editor.CurrentConnection.DeleteResource(tempname); }
catch {}
}
item.Status = OSGeo.MapGuide.MaestroAPI.LengthyOperationCallbackArgs.LengthyOperationItem.OperationStatus.Success;
validitems.Add(item.Itempath);
}
catch (Exception ex)
{
string s = ex.Message;
item.Status = OSGeo.MapGuide.MaestroAPI.LengthyOperationCallbackArgs.LengthyOperationItem.OperationStatus.Failure;
}
if (callback != null)
{
callback(this, args);
if (args.Cancel)
return false;
}
args.Index++;
} while(!args.Cancel && args.Index < args.Items.Length);
}
//All done, if we have any working data, build the resource
if (!args.Cancel)
{
if (m_feature.ConfigurationDocument != null && m_feature.ConfigurationDocument.Trim().Length == 0)
m_feature.ConfigurationDocument = null;
System.Text.StringBuilder sb2 = new System.Text.StringBuilder();
foreach(string s in toplevels.Keys)
sb2.Append(string.Format(TEMPLATE_LOC, s, ((System.Text.StringBuilder)toplevels[s]).ToString()));
string schemaXml = string.Format(TEMPLATE_SMAP, sb2.ToString());
string configdoc = string.Format(System.Globalization.CultureInfo.InvariantCulture, TEMPLATE_CFG, minx, miny, maxx, maxy);
System.Xml.XmlDocument doc1 = new System.Xml.XmlDocument();
System.Xml.XmlDocument doc2 = new System.Xml.XmlDocument();
doc1.LoadXml(configdoc);
doc2.LoadXml(schemaXml);
System.Xml.XmlNode newNode = doc1.ImportNode(doc2.DocumentElement, true);
System.Xml.XmlNode oldNode = doc1.GetElementsByTagName("SchemaMapping")[0];
oldNode.ParentNode.ReplaceChild(newNode, oldNode);
if (doc1.FirstChild as System.Xml.XmlDeclaration != null)
doc1.RemoveChild(doc1.FirstChild);
doc1.PrependChild(doc1.CreateXmlDeclaration("1.0", "utf-8", null));
System.IO.MemoryStream ms = new System.IO.MemoryStream();
doc1.Save(ms);
ms = OSGeo.MapGuide.MaestroAPI.Utility.RemoveUTF8BOM(ms);
ms.Position = 0;
string docname = m_feature.ConfigurationDocument == null ? "config" : m_feature.ConfigurationDocument;
m_editor.CurrentConnection.SetResourceData(m_feature.ResourceId, docname, OSGeo.MapGuide.MaestroAPI.ResourceDataType.Stream, ms);
m_feature.ConfigurationDocument = docname;
m_editor.CurrentConnection.SaveResource(m_feature);
m_files = (string[])validitems.ToArray(typeof(string));
}
m_editor.HasChanged();
return !args.Cancel;
}
private void GetBounds(Hashtable files, out double minx, out double maxx, out double miny, out double maxy)
{
minx = miny = double.MaxValue;
maxx = maxy = double.MinValue;
XmlDocument doc = new XmlDocument();
foreach(System.Text.StringBuilder sb in files.Values)
{
doc.LoadXml("" + sb.ToString() + "");
XmlNamespaceManager nm = new XmlNamespaceManager(doc.NameTable);
nm.AddNamespace("e", "http://fdogrfp.osgeo.org/schemas");
foreach(XmlNode n in doc.SelectNodes("e:test/e:Feature/e:Band/e:Image/e:Bounds", nm))
{
double coord;
if (n["MinX"] != null && double.TryParse(n["MinX"].InnerText, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out coord))
{
minx = Math.Min(coord, minx);
maxx = Math.Max(coord, maxx);
}
if (n["MaxX"] != null && double.TryParse(n["MaxX"].InnerText, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out coord))
{
minx = Math.Min(coord, minx);
maxx = Math.Max(coord, maxx);
}
if (n["MinY"] != null && double.TryParse(n["MinY"].InnerText, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out coord))
{
miny = Math.Min(coord, miny);
maxy = Math.Max(coord, maxy);
}
if (n["MaxY"] != null && double.TryParse(n["MaxY"].InnerText, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out coord))
{
miny = Math.Min(coord, miny);
maxy = Math.Max(coord, maxy);
}
}
}
}
public Hashtable GetFilelist()
{
Hashtable filenames = new Hashtable();
//Try to read the existing data resource and filelist
if (m_feature.ConfigurationDocument != null && m_feature.ConfigurationDocument.Trim().Length != 0)
{
try
{
XmlDocument doc = new XmlDocument();
doc.Load(m_editor.CurrentConnection.GetResourceData(m_feature.ResourceId, m_feature.ConfigurationDocument));
XmlNamespaceManager nm = new XmlNamespaceManager(doc.NameTable);
nm.AddNamespace("e", "http://fdogrfp.osgeo.org/schemas");
nm.AddNamespace("gml", "http://www.opengis.net/gml");
//Not sure why some have double "complexType" entries
ArrayList totalNodes = new ArrayList();
foreach(XmlNode n in doc["fdo:DataStore"].SelectNodes("e:SchemaMapping/e:complexType/e:complexType/e:RasterDefinition/e:Location", nm))
totalNodes.Add(n);
foreach(XmlNode n in doc["fdo:DataStore"].SelectNodes("e:SchemaMapping/e:complexType/e:RasterDefinition/e:Location", nm))
totalNodes.Add(n);
foreach(XmlNode n in totalNodes)
foreach(XmlNode img in n.SelectNodes("e:Feature", nm))
{
string fullname = n.Attributes["name"].Value;
if (fullname.IndexOf("\\") > 0 && !fullname.EndsWith("\\"))
fullname += "\\";
else if (fullname.IndexOf("/") > 0 && !fullname.EndsWith("/"))
fullname += "/";
if (img["Band"] == null || img["Band"]["Image"] == null)
continue;
XmlNode inner = img["Band"]["Image"];
fullname += inner.Attributes["name"].Value;
filenames[fullname] = img.OuterXml;
}
}
catch
{
filenames = new Hashtable();
}
}
return filenames;
}
}
}