[mapguide-commits] r5431 - in sandbox/maestro-3.0: MaestroAPITests
OSGeo.MapGuide.MaestroAPI OSGeo.MapGuide.MaestroAPI/Commands
OSGeo.MapGuide.MaestroAPI/Mapping OSGeo.MapGuide.MaestroAPI/Services
OSGeo.MapGuide.MaestroAPI.Http
OSGeo.MapGuide.MaestroAPI.Http/Commands
OSGeo.MapGuide.MaestroAPI.Native
OSGeo.MapGuide.MaestroAPI.Native/Commands
svn_mapguide at osgeo.org
svn_mapguide at osgeo.org
Thu Dec 2 04:59:15 EST 2010
Author: jng
Date: 2010-12-02 01:59:15 -0800 (Thu, 02 Dec 2010)
New Revision: 5431
Added:
sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Http/Commands/
sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Http/Commands/HttpGetResourceContents.cs
sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Native/Commands/LocalGetResourceContents.cs
sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/Commands/IGetResourceContents.cs
Modified:
sandbox/maestro-3.0/MaestroAPITests/HttpConnectionTests.cs
sandbox/maestro-3.0/MaestroAPITests/RuntimeMapTests.cs
sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Http/HttpCapabilities.cs
sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Http/HttpServerConnection.cs
sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Http/OSGeo.MapGuide.MaestroAPI.Http.csproj
sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Native/LocalNativeCapabilities.cs
sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Native/LocalNativeConnection.cs
sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Native/OSGeo.MapGuide.MaestroAPI.Native.csproj
sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/
sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/Commands/CommandType.cs
sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/Mapping/RuntimeMap.cs
sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/OSGeo.MapGuide.MaestroAPI.csproj
sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/Services/IMappingService.cs
Log:
This submission includes the following changes:
- Include a IGetResourceContents custom command
- Provide a HTTP implementation (using ThreadPool)
- Provide a Local implementation (using the MgResourceService.GetResourceContents() API introduced in 2.2). Consequently this command is disabled if connecting to a pre-2.2 MapGuide Server
- Fix the cause of Rendering problems in RuntimeMap. I had a statement that set the MetersPerUnit back to 1.0 after setting our custom value! Yes. Very smart!
- Reintroduce the RenderDynamicOverlay API
- Add convenience Render methods to RuntimeMap
- Update unit tests to test normal and convenience render methods
- Update RuntimeMap to use batching via IGetResourceContents to pre-cache all required layer definitions if available. Unit tests indicate performance gains range from around 200% for HTTP and 300% for Local connections (as expected when using the GetResourceContents() API introduced in MG 2.2)
Modified: sandbox/maestro-3.0/MaestroAPITests/HttpConnectionTests.cs
===================================================================
--- sandbox/maestro-3.0/MaestroAPITests/HttpConnectionTests.cs 2010-12-02 06:45:19 UTC (rev 5430)
+++ sandbox/maestro-3.0/MaestroAPITests/HttpConnectionTests.cs 2010-12-02 09:59:15 UTC (rev 5431)
@@ -61,24 +61,24 @@
Assert.IsNotNull(props);
Assert.AreEqual(props.Length, 2);
- Assert.IsTrue(Array.IndexOf<string>(props, "UserAgent") >= 0);
- Assert.IsTrue(Array.IndexOf<string>(props, "BaseUrl") >= 0);
+ Assert.IsTrue(Array.IndexOf<string>(props, HttpServerConnection.PROP_USER_AGENT) >= 0);
+ Assert.IsTrue(Array.IndexOf<string>(props, HttpServerConnection.PROP_BASE_URL) >= 0);
//It is of type string
- var type = isvc.GetCustomPropertyType("UserAgent");
+ var type = isvc.GetCustomPropertyType(HttpServerConnection.PROP_USER_AGENT);
Assert.AreEqual(type, typeof(string));
- type = isvc.GetCustomPropertyType("BaseUrl");
+ type = isvc.GetCustomPropertyType(HttpServerConnection.PROP_BASE_URL);
Assert.AreEqual(type, typeof(string));
//We can set and get it
- isvc.SetCustomProperty("UserAgent", "MapGuide Maestro API Unit Test Fixture");
- var agent = (string)isvc.GetCustomProperty("UserAgent");
+ isvc.SetCustomProperty(HttpServerConnection.PROP_USER_AGENT, "MapGuide Maestro API Unit Test Fixture");
+ var agent = (string)isvc.GetCustomProperty(HttpServerConnection.PROP_USER_AGENT);
Assert.AreEqual(agent, "MapGuide Maestro API Unit Test Fixture");
//BaseUrl is read-only
try
{
- isvc.SetCustomProperty("BaseUrl", "http://mylocalhost/mapguide");
+ isvc.SetCustomProperty(HttpServerConnection.PROP_BASE_URL, "http://mylocalhost/mapguide");
Assert.Fail("Should've thrown exception");
}
catch { }
Modified: sandbox/maestro-3.0/MaestroAPITests/RuntimeMapTests.cs
===================================================================
--- sandbox/maestro-3.0/MaestroAPITests/RuntimeMapTests.cs 2010-12-02 06:45:19 UTC (rev 5430)
+++ sandbox/maestro-3.0/MaestroAPITests/RuntimeMapTests.cs 2010-12-02 09:59:15 UTC (rev 5431)
@@ -323,11 +323,46 @@
map.Save();
//Render default
- using (var stream = mapSvc.RenderRuntimeMap(map.ResourceID, map.ViewCenter.X, map.ViewCenter.Y, map.ViewScale, map.DisplayWidth, map.DisplayHeight, map.DisplayDpi, "PNG"))
+ RenderAndVerify(mapSvc, map, "TestRender75k.png", "PNG");
+ RenderAndVerifyConvenience(map, "TestRender75kConvenience.png", "PNG");
+ RenderDynamicOverlayAndVerify(mapSvc, map, "TestRenderOverlay75k.png", "PNG");
+ RenderDynamicOverlayAndVerifyConvenience(map, "TestRenderOverlay75kConvenience.png", "PNG");
+
+ //Turn off parcels
+ var rail = map.GetLayerByName("Rail");
+ Assert.NotNull(rail);
+ rail.Visible = false;
+ map.Save();
+
+ //Render again
+ RenderAndVerify(mapSvc, map, "TestRender75k_NoRail.png", "PNG");
+ RenderAndVerifyConvenience(map, "TestRender75kConvenience_NoRail.png", "PNG");
+ RenderDynamicOverlayAndVerify(mapSvc, map, "TestRenderOverlay75k_NoRail.png", "PNG");
+ RenderDynamicOverlayAndVerifyConvenience(map, "TestRenderOverlay75kConvenience_NoRail.png", "PNG");
+
+ //Turn Rail back on
+ rail = null;
+ rail = map.GetLayerByName("Rail");
+ Assert.NotNull(rail);
+ rail.Visible = true;
+ map.Save();
+
+ //Render again
+ RenderAndVerify(mapSvc, map, "TestRender75k_RailBackOn.png", "PNG");
+ RenderAndVerifyConvenience(map, "TestRender75kConvenience_RailBackOn.png", "PNG");
+ RenderDynamicOverlayAndVerify(mapSvc, map, "TestRenderOverlay75k_RailBackOn.png", "PNG");
+ RenderDynamicOverlayAndVerifyConvenience(map, "TestRenderOverlay75kConvenience_RailBackOn.png", "PNG");
+ }
+
+ #region render helpers
+
+ private static void RenderDynamicOverlayAndVerifyConvenience(RuntimeMap map, string fileName, string format)
+ {
+ using (var stream = map.RenderDynamicOverlay(format, true))
{
using (var ms = new MemoryStream())
using (var ms2 = new MemoryStream())
- using (var fs = new FileStream("TestRender75k.png", FileMode.OpenOrCreate))
+ using (var fs = new FileStream(fileName, FileMode.OpenOrCreate))
{
Utility.CopyStream(stream, ms);
Utility.CopyStream(ms, ms2);
@@ -344,19 +379,40 @@
}
}
}
+ }
- //Turn off parcels
- var rail = map.GetLayerByName("Rail");
- Assert.NotNull(rail);
- rail.Visible = false;
- map.Save();
+ private static void RenderDynamicOverlayAndVerify(IMappingService mapSvc, RuntimeMap map, string fileName, string format)
+ {
+ using (var stream = mapSvc.RenderDynamicOverlay(map, map.Selection, format))
+ {
+ using (var ms = new MemoryStream())
+ using (var ms2 = new MemoryStream())
+ using (var fs = new FileStream(fileName, FileMode.OpenOrCreate))
+ {
+ Utility.CopyStream(stream, ms);
+ Utility.CopyStream(ms, ms2);
+ Utility.CopyStream(ms, fs);
+ //See if System.Drawing.Image accepts this
+ try
+ {
+ using (var img = System.Drawing.Image.FromStream(ms))
+ { }
+ }
+ catch (Exception ex)
+ {
+ Assert.Fail(ex.Message);
+ }
+ }
+ }
+ }
- //Render again
- using (var stream = mapSvc.RenderRuntimeMap(map.ResourceID, map.ViewCenter.X, map.ViewCenter.Y, map.ViewScale, map.DisplayWidth, map.DisplayHeight, map.DisplayDpi, "PNG"))
+ private static void RenderAndVerify(IMappingService mapSvc, RuntimeMap map, string fileName, string format)
+ {
+ using (var stream = mapSvc.RenderRuntimeMap(map.ResourceID, map.ViewCenter.X, map.ViewCenter.Y, map.ViewScale, map.DisplayWidth, map.DisplayHeight, map.DisplayDpi, format))
{
using (var ms = new MemoryStream())
using (var ms2 = new MemoryStream())
- using (var fs = new FileStream("TestRender75k_NoRail.png", FileMode.OpenOrCreate))
+ using (var fs = new FileStream(fileName, FileMode.OpenOrCreate))
{
Utility.CopyStream(stream, ms);
Utility.CopyStream(ms, ms2);
@@ -373,20 +429,15 @@
}
}
}
+ }
- //Turn Rail back on
- rail = null;
- rail = map.GetLayerByName("Rail");
- Assert.NotNull(rail);
- rail.Visible = true;
- map.Save();
-
- //Render again
- using (var stream = mapSvc.RenderRuntimeMap(map.ResourceID, map.ViewCenter.X, map.ViewCenter.Y, map.ViewScale, map.DisplayWidth, map.DisplayHeight, map.DisplayDpi, "PNG"))
+ private static void RenderAndVerifyConvenience(RuntimeMap map, string fileName, string format)
+ {
+ using (var stream = map.Render(format))
{
using (var ms = new MemoryStream())
using (var ms2 = new MemoryStream())
- using (var fs = new FileStream("TestRender75k_RailBackOn.png", FileMode.OpenOrCreate))
+ using (var fs = new FileStream(fileName, FileMode.OpenOrCreate))
{
Utility.CopyStream(stream, ms);
Utility.CopyStream(ms, ms2);
@@ -405,6 +456,8 @@
}
}
+ #endregion
+
public virtual void TestRender12k()
{
//Render a map of sheboygan at 12k
@@ -438,27 +491,10 @@
map.Save();
//Render default
- using (var stream = mapSvc.RenderRuntimeMap(map.ResourceID, map.ViewCenter.X, map.ViewCenter.Y, map.ViewScale, map.DisplayWidth, map.DisplayHeight, map.DisplayDpi, "PNG"))
- {
- using (var ms = new MemoryStream())
- using (var ms2 = new MemoryStream())
- using (var fs = new FileStream("TestRender12k.png", FileMode.OpenOrCreate))
- {
- Utility.CopyStream(stream, ms);
- Utility.CopyStream(ms, ms2);
- Utility.CopyStream(ms, fs);
- //See if System.Drawing.Image accepts this
- try
- {
- using (var img = System.Drawing.Image.FromStream(ms))
- { }
- }
- catch (Exception ex)
- {
- Assert.Fail(ex.Message);
- }
- }
- }
+ RenderAndVerify(mapSvc, map, "TestRender12k.png", "PNG");
+ RenderAndVerifyConvenience(map, "TestRender12kConvenience.png", "PNG");
+ RenderDynamicOverlayAndVerify(mapSvc, map, "TestRenderOverlay12k.png", "PNG");
+ RenderDynamicOverlayAndVerifyConvenience(map, "TestRenderOverlay12kConvenience.png", "PNG");
//Turn off parcels
var parcels = map.GetLayerByName("Parcels");
@@ -467,27 +503,10 @@
map.Save();
//Render again
- using (var stream = mapSvc.RenderRuntimeMap(map.ResourceID, map.ViewCenter.X, map.ViewCenter.Y, map.ViewScale, map.DisplayWidth, map.DisplayHeight, map.DisplayDpi, "PNG"))
- {
- using (var ms = new MemoryStream())
- using (var ms2 = new MemoryStream())
- using (var fs = new FileStream("TestRender12k_NoParcels.png", FileMode.OpenOrCreate))
- {
- Utility.CopyStream(stream, ms);
- Utility.CopyStream(ms, ms2);
- Utility.CopyStream(ms, fs);
- //See if System.Drawing.Image accepts this
- try
- {
- using (var img = System.Drawing.Image.FromStream(ms))
- { }
- }
- catch (Exception ex)
- {
- Assert.Fail(ex.Message);
- }
- }
- }
+ RenderAndVerify(mapSvc, map, "TestRender12k_NoParcels.png", "PNG");
+ RenderAndVerifyConvenience(map, "TestRender12kConvenience_NoParcels.png", "PNG");
+ RenderDynamicOverlayAndVerify(mapSvc, map, "TestRenderOverlay12k_NoParcels.png", "PNG");
+ RenderDynamicOverlayAndVerifyConvenience(map, "TestRenderOverlay12kConvenience_NoParcels.png", "PNG");
//Turn parcels back on
parcels = null;
@@ -497,27 +516,10 @@
map.Save();
//Render again
- using (var stream = mapSvc.RenderRuntimeMap(map.ResourceID, map.ViewCenter.X, map.ViewCenter.Y, map.ViewScale, map.DisplayWidth, map.DisplayHeight, map.DisplayDpi, "PNG"))
- {
- using (var ms = new MemoryStream())
- using (var ms2 = new MemoryStream())
- using (var fs = new FileStream("TestRender12k_ParcelsBackOn.png", FileMode.OpenOrCreate))
- {
- Utility.CopyStream(stream, ms);
- Utility.CopyStream(ms, ms2);
- Utility.CopyStream(ms, fs);
- //See if System.Drawing.Image accepts this
- try
- {
- using (var img = System.Drawing.Image.FromStream(ms))
- { }
- }
- catch (Exception ex)
- {
- Assert.Fail(ex.Message);
- }
- }
- }
+ RenderAndVerify(mapSvc, map, "TestRender12k_ParcelsBackOn.png", "PNG");
+ RenderAndVerifyConvenience(map, "TestRender12kConvenience_ParcelsBackOn.png", "PNG");
+ RenderDynamicOverlayAndVerify(mapSvc, map, "TestRenderOverlay12k_ParcelsBackOn.png", "PNG");
+ RenderDynamicOverlayAndVerifyConvenience(map, "TestRenderOverlay12kConvenience_ParcelsBackOn.png", "PNG");
}
public virtual void TestResourceEvents()
@@ -547,6 +549,13 @@
public virtual void TestLargeMapCreatePerformance()
{
+ TestMapCreate(50, 10);
+ TestMapCreate(100, 25);
+ TestMapCreate(200, 50);
+ }
+
+ private void TestMapCreate(int layerSize, int groupSize)
+ {
//Create a 200 layer, 50 group map. This is not part of the benchmark
var mdf = ObjectFactory.CreateMapDefinition(_conn, "LargeMap");
string root = "Library://UnitTests/LargeMapTest/";
@@ -561,9 +570,9 @@
Utility.CopyStream(fs, ms);
}
- int step = 4;
+ int step = layerSize / groupSize;
int g = 0;
- for (int i = 0; i < 200; i++)
+ for (int i = 0; i < layerSize; i++)
{
if (i % step == 0)
g++;
@@ -594,13 +603,12 @@
var map = mapSvc.CreateMap(mid, mdf, 1.0);
sw.Stop();
- string msg = "Create Map time for 200 layer, 50 group map: " + sw.ElapsedMilliseconds + "ms";
+ string msg = "Create Map time for " + layerSize + " layer, " + groupSize + " group map: " + sw.ElapsedMilliseconds + "ms";
Trace.WriteLine(msg);
- Console.WriteLine(msg);
}
}
- [TestFixture]
+ [TestFixture(Ignore = true)]
public class HttpRuntimeMapTests : RuntimeMapTests
{
protected override IServerConnection CreateTestConnection()
@@ -648,7 +656,7 @@
}
}
- [TestFixture(Ignore = true)]
+ [TestFixture]
public class LocalRuntimeMapTests : RuntimeMapTests
{
protected override IServerConnection CreateTestConnection()
@@ -660,6 +668,12 @@
}
[Test]
+ public override void TestResourceEvents()
+ {
+ base.TestResourceEvents();
+ }
+
+ [Test]
public override void TestCreate()
{
base.TestCreate();
Property changes on: sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI
___________________________________________________________________
Modified: svn:ignore
- bin
obj
+ bin
obj
*.user
Modified: sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/Commands/CommandType.cs
===================================================================
--- sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/Commands/CommandType.cs 2010-12-02 06:45:19 UTC (rev 5430)
+++ sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/Commands/CommandType.cs 2010-12-02 09:59:15 UTC (rev 5431)
@@ -28,5 +28,6 @@
/// </summary>
public enum CommandType : int
{
+ GetResourceContents
}
}
Added: sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/Commands/IGetResourceContents.cs
===================================================================
--- sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/Commands/IGetResourceContents.cs (rev 0)
+++ sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/Commands/IGetResourceContents.cs 2010-12-02 09:59:15 UTC (rev 5431)
@@ -0,0 +1,39 @@
+#region Disclaimer / License
+// Copyright (C) 2010, Jackie Ng
+// http://trac.osgeo.org/mapguide/wiki/maestro, jumpinjackie at gmail.com
+//
+// 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.Collections.Generic;
+using System.Text;
+using OSGeo.MapGuide.MaestroAPI.Resource;
+
+namespace OSGeo.MapGuide.MaestroAPI.Commands
+{
+ /// <summary>
+ /// Defines a command to get a set of resource contents in a single batch
+ /// </summary>
+ public interface IGetResourceContents : ICommand
+ {
+ /// <summary>
+ /// Gets the resource content of the specified resources
+ /// </summary>
+ /// <param name="resourceIds"></param>
+ /// <returns></returns>
+ Dictionary<string, IResource> Execute(IEnumerable<string> resourceIds);
+ }
+}
Modified: sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/Mapping/RuntimeMap.cs
===================================================================
--- sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/Mapping/RuntimeMap.cs 2010-12-02 06:45:19 UTC (rev 5430)
+++ sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/Mapping/RuntimeMap.cs 2010-12-02 09:59:15 UTC (rev 5431)
@@ -28,6 +28,8 @@
using OSGeo.MapGuide.MaestroAPI.Resource;
using System.ComponentModel;
using OSGeo.MapGuide.ObjectModels.LayerDefinition;
+using System.Diagnostics;
+using OSGeo.MapGuide.MaestroAPI.Commands;
namespace OSGeo.MapGuide.MaestroAPI.Mapping
{
@@ -38,8 +40,20 @@
/// </summary>
/// <remarks>
/// If you want to use this instance with the Rendering Service APIs, it is important to set the correct
- /// meters per unit value before calling the <see cref="OSGeo.MapGuide.MaestroAPI.Mapping.RuntimeMap.Save"/> method, as an incorrect meters
+ /// meters per unit value before calling the <see cref="T:OSGeo.MapGuide.MaestroAPI.Mapping.RuntimeMap.Save"/> method, as an incorrect meters
/// per unit value will produce incorrect images.
+ ///
+ /// Also note that to improve the creation performance, certain implementations of <see cref="T:OSGeo.MapGuide.MaestroAPI.IServerConnection"/>
+ /// offer a <see cref="T:OSGeo.MapGuide.MaestroAPI.Mapping.IRuntimeMapSetup"/> helper to return a series of layer definitions in a batch (fetching
+ /// layer definitions one at a time is the main performance bottleneck for large maps), batching can improve creation times by:
+ ///
+ /// - HTTP: 2x
+ /// - Local: 3x (if using MapGuide 2.2 APIs. As this takes advantage of the GetResourceContents() API introduced in 2.2). For older versions of MapGuide there is no batching.
+ ///
+ /// In particular, the HTTP implementation of <see cref="T:OSGeo.MapGuide.MaestroAPI.IServerConection"/> uses the <see cref="T:System.Threading.ThreadPool"/>
+ /// class to fetch multiple layer definitions in parallel. If your code uses this implementation, be aware of this face and the performance implications
+ /// involved, as an excessively large thread pool size may negatively affect stability of your MapGuide Server.
+ ///
/// </remarks>
/// <example>
/// How to create a <see cref="RuntimeMap"/> with the correct meters per unit value using the MgCoordinateSystem API
@@ -123,6 +137,9 @@
/// </summary>
protected Dictionary<string, RuntimeMapLayer> _layerIdMap;
+ private IMappingService _mapSvc;
+ private IGetResourceContents _getRes;
+
internal RuntimeMap(IServerConnection conn)
{
_disableChangeTracking = true;
@@ -134,24 +151,46 @@
_finiteDisplayScales = new double[0];
this.ResourceService = conn.ResourceService;
this.FeatureService = conn.FeatureService;
+ if (Array.IndexOf(conn.Capabilities.SupportedServices, (int)ServiceType.Mapping) >= 0)
+ {
+ _mapSvc = (IMappingService)conn.GetService((int)ServiceType.Mapping);
+ }
+ if (Array.IndexOf(conn.Capabilities.SupportedCommands, (int)CommandType.GetResourceContents) >= 0)
+ {
+ _getRes = (IGetResourceContents)conn.CreateCommand((int)CommandType.GetResourceContents);
+ }
_layers = new List<RuntimeMapLayer>();
_groups = new List<RuntimeMapGroup>();
_layerIdMap = new Dictionary<string, RuntimeMapLayer>();
this.Selection = new MapSelection(this);
}
+ static IEnumerable<string> GetLayerIds(IMapDefinition mdf)
+ {
+ foreach (var layer in mdf.MapLayer)
+ {
+ yield return layer.ResourceId;
+ }
+ if (mdf.BaseMap != null)
+ {
+ foreach (var group in mdf.BaseMap.BaseMapLayerGroup)
+ {
+ foreach (var layer in group.BaseMapLayer)
+ {
+ yield return layer.ResourceId;
+ }
+ }
+ }
+ }
+
/// <summary>
/// Initializes a new instance of the <see cref="RuntimeMap"/> class.
/// </summary>
/// <param name="mdf">The map definition to create this map from.</param>
+ /// <param name="metersPerUnit">The meters per unit value</param>
internal RuntimeMap(IMapDefinition mdf, double metersPerUnit)
: this(mdf.CurrentConnection)
{
- //TODO: Performance would be greatly improved if we can take advantage of
- //the new GetResourceContents() API in 2.2. But can we do this via the combination
- //of reflection and assembly binding redirection seeing as we are referencing
- //a 2.0.x assembly?
-
this.MetersPerUnit = metersPerUnit;
this.MapDefinition = mdf.ResourceID;
@@ -159,9 +198,26 @@
this.DataExtent = mdf.Extents.Clone();
this.BackgroundColor = mdf.BackgroundColor;
this.CoordinateSystem = mdf.CoordinateSystem;
- this.MetersPerUnit = 1.0;
+
//TODO: infer real mpu from coordinate system
+ //If a setup helper exists, use it to get required layers in a single
+ //batch. Eliminating lots of chatter for really large maps.
+ if (_getRes != null)
+ {
+ Trace.TraceInformation("[RuntimeMap.ctor]: Batching layer requests");
+ var res = _getRes.Execute(GetLayerIds(mdf));
+ //Pre-populate layer def cache so GetLayerDefinition() returns these
+ //instead of making a new request
+ foreach (var key in res.Keys)
+ {
+ var layer = res[key] as ILayerDefinition;
+ if (layer != null)
+ layerDefinitionCache.Add(key, layer);
+ }
+ Trace.TraceInformation("[RuntimeMap.ctor]: {0} layers pre-cached", layerDefinitionCache.Count);
+ }
+
int dispIndex = 0;
//Load map layers
foreach (var layer in mdf.MapLayer)
@@ -1181,5 +1237,46 @@
}
#endregion
+
+ #region convenience methods
+ /// <summary>
+ /// Convenience method for rendering a bitmap of the current map
+ /// </summary>
+ /// <param name="format"></param>
+ /// <returns></returns>
+ public System.IO.Stream Render(string format)
+ {
+ if (_mapSvc == null)
+ throw new NotSupportedException();
+
+ return _mapSvc.RenderRuntimeMap(
+ this.ResourceID,
+ this.ViewCenter.X,
+ this.ViewCenter.Y,
+ this.ViewScale,
+ this.DisplayWidth,
+ this.DisplayHeight,
+ this.DisplayDpi,
+ format);
+ }
+
+ /// <summary>
+ /// Convenience method for rendering a dynamic overlay of the current map
+ /// </summary>
+ /// <param name="format"></param>
+ /// <param name="keepSelection"></param>
+ /// <returns></returns>
+ public System.IO.Stream RenderDynamicOverlay(string format, bool keepSelection)
+ {
+ if (_mapSvc == null)
+ throw new NotSupportedException();
+
+ return _mapSvc.RenderDynamicOverlay(
+ this,
+ this.Selection,
+ format,
+ keepSelection);
+ }
+ #endregion
}
}
Modified: sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/OSGeo.MapGuide.MaestroAPI.csproj
===================================================================
--- sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/OSGeo.MapGuide.MaestroAPI.csproj 2010-12-02 06:45:19 UTC (rev 5430)
+++ sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/OSGeo.MapGuide.MaestroAPI.csproj 2010-12-02 09:59:15 UTC (rev 5431)
@@ -167,6 +167,7 @@
<Link>GlobalAssemblyInfo.cs</Link>
</Compile>
<Compile Include="BindingListExtensions.cs" />
+ <Compile Include="Commands\IGetResourceContents.cs" />
<Compile Include="Capability\ConnectionCapabilities.cs" />
<Compile Include="Check.cs" />
<Compile Include="Commands\CommandType.cs" />
Modified: sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/Services/IMappingService.cs
===================================================================
--- sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/Services/IMappingService.cs 2010-12-02 06:45:19 UTC (rev 5430)
+++ sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/Services/IMappingService.cs 2010-12-02 09:59:15 UTC (rev 5431)
@@ -55,9 +55,9 @@
/// <returns></returns>
RuntimeMap OpenMap(string runtimeMapResourceId);
- //System.IO.Stream RenderDynamicOverlay(RuntimeMap map, MapSelection selection, string format);
+ System.IO.Stream RenderDynamicOverlay(RuntimeMap map, MapSelection selection, string format);
- //System.IO.Stream RenderDynamicOverlay(RuntimeMap map, MapSelection selection, string format, bool keepSelection);
+ System.IO.Stream RenderDynamicOverlay(RuntimeMap map, MapSelection selection, string format, bool keepSelection);
/// <summary>
/// Renders the runtime map.
Added: sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Http/Commands/HttpGetResourceContents.cs
===================================================================
--- sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Http/Commands/HttpGetResourceContents.cs (rev 0)
+++ sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Http/Commands/HttpGetResourceContents.cs 2010-12-02 09:59:15 UTC (rev 5431)
@@ -0,0 +1,99 @@
+#region Disclaimer / License
+// Copyright (C) 2010, Jackie Ng
+// http://trac.osgeo.org/mapguide/wiki/maestro, jumpinjackie at gmail.com
+//
+// 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.Collections.Generic;
+using System.Text;
+using OSGeo.MapGuide.MaestroAPI.Mapping;
+using OSGeo.MapGuide.MaestroAPI.Services;
+using OSGeo.MapGuide.MaestroAPI.Resource;
+using System.Net;
+using System.Threading;
+using System.Diagnostics;
+using OSGeo.MapGuide.MaestroAPI.Commands;
+
+namespace OSGeo.MapGuide.MaestroAPI.Http.Commands
+{
+ public class HttpGetResourceContents : IGetResourceContents
+ {
+ private readonly IResourceService _resSvc;
+
+ public HttpGetResourceContents(IResourceService resSvc)
+ {
+ _resSvc = resSvc;
+ _completed = new Dictionary<string, IResource>();
+ }
+
+ private Dictionary<string, IResource> _completed;
+
+ private readonly object SyncRoot = new object();
+
+ private void PutCompleted(IResource res)
+ {
+ lock (SyncRoot)
+ {
+ _completed.Add(res.ResourceID, res);
+ }
+ }
+
+ public Dictionary<string, IResource> Execute(IEnumerable<string> resourceIds)
+ {
+ _completed.Clear();
+ List<string> workItems = new List<string>(resourceIds);
+ int completed = 0;
+
+ foreach (var resId in workItems)
+ {
+ //Closures referencing iterator variables are bad mmkay?
+ string rid = resId;
+
+ //NOTE: Multi-threaded code is my weakness. So I wouldn't be surprised
+ //if this has some subtle bug due to multi-threading. However, I have
+ //stuck to basic rules of thumb in implementing this (ie. Do not let threads
+ //manipulate shared state!). The whole code path of the
+ //IResourceService.GetResource() implementation does not touch any shared
+ //state. So I say with minor confidence that this should work without problems.
+ ThreadPool.QueueUserWorkItem((obj) =>
+ {
+ try
+ {
+ IResource res = _resSvc.GetResource(rid);
+ PutCompleted(res);
+ }
+ finally
+ {
+ Interlocked.Increment(ref completed);
+ }
+ });
+ }
+
+ //Wait until all completed
+ while (completed < workItems.Count)
+ Thread.Sleep(20);
+
+ return _completed;
+ }
+
+ public IServerConnection Parent
+ {
+ get;
+ private set;
+ }
+ }
+}
Modified: sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Http/HttpCapabilities.cs
===================================================================
--- sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Http/HttpCapabilities.cs 2010-12-02 06:45:19 UTC (rev 5430)
+++ sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Http/HttpCapabilities.cs 2010-12-02 09:59:15 UTC (rev 5431)
@@ -23,6 +23,7 @@
using OSGeo.MapGuide.MaestroAPI.Services;
using OSGeo.MapGuide.MaestroAPI.Exceptions;
using OSGeo.MapGuide.MaestroAPI.Capability;
+using OSGeo.MapGuide.MaestroAPI.Commands;
namespace OSGeo.MapGuide.MaestroAPI.Http
{
@@ -37,6 +38,7 @@
//TODO: Work out what this can/can't do
return new int[]
{
+ (int)CommandType.GetResourceContents
};
}
}
Modified: sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Http/HttpServerConnection.cs
===================================================================
--- sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Http/HttpServerConnection.cs 2010-12-02 06:45:19 UTC (rev 5430)
+++ sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Http/HttpServerConnection.cs 2010-12-02 09:59:15 UTC (rev 5431)
@@ -39,6 +39,7 @@
using System.Text;
using System.Collections.Generic;
using OSGeo.MapGuide.ObjectModels.ApplicationDefinition_1_0_0;
+using OSGeo.MapGuide.MaestroAPI.Http.Commands;
namespace OSGeo.MapGuide.MaestroAPI
{
@@ -68,7 +69,6 @@
internal HttpServerConnection()
: base()
{
- //m_wc = new WebClient();
m_cachedProviderCapabilities = new Hashtable();
}
@@ -851,7 +851,12 @@
{
Utility.CopyStream(ms, rs);
rs.Flush();
- return req.GetResponse().GetResponseStream();
+ var resp = req.GetResponse();
+ var hwr = resp as HttpWebResponse;
+ if (hwr != null)
+ LogResponse(hwr);
+
+ return resp.GetResponseStream();
}
}
@@ -874,7 +879,13 @@
{
Utility.CopyStream(ms, rs);
rs.Flush();
- return req.GetResponse().GetResponseStream();
+ var resp = req.GetResponse();
+
+ var hwr = resp as HttpWebResponse;
+ if (hwr != null)
+ LogResponse(hwr);
+
+ return resp.GetResponseStream();
}
#endif
@@ -899,7 +910,13 @@
{
Utility.CopyStream(ms, rs);
rs.Flush();
- return req.GetResponse().GetResponseStream();
+ var resp = req.GetResponse();
+ var hwr = resp as HttpWebResponse;
+
+ if (hwr != null)
+ LogResponse(hwr);
+
+ return resp.GetResponseStream();
}
#endif
@@ -1443,12 +1460,22 @@
public const string PROP_USER_AGENT = "UserAgent";
public const string PROP_BASE_URL = "BaseUrl";
-
+
public override string[] GetCustomPropertyNames()
{
return new string[] { PROP_USER_AGENT, PROP_BASE_URL };
}
+ /// <summary>
+ /// Gets or sets the number of worker threads to spawn when initializing
+ /// a runtime map
+ /// </summary>
+ public int RuntimeMapWorkerCount
+ {
+ get;
+ set;
+ }
+
public override Type GetCustomPropertyType(string name)
{
if (name == PROP_USER_AGENT)
@@ -1655,5 +1682,13 @@
return this.DeserializeObject<SiteInformation>(s);
}
}
+
+ public override ICommand CreateCommand(int cmdType)
+ {
+ CommandType ct = (CommandType)cmdType;
+ if (ct == CommandType.GetResourceContents)
+ return new HttpGetResourceContents(this);
+ return base.CreateCommand(cmdType);
+ }
}
}
Modified: sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Http/OSGeo.MapGuide.MaestroAPI.Http.csproj
===================================================================
--- sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Http/OSGeo.MapGuide.MaestroAPI.Http.csproj 2010-12-02 06:45:19 UTC (rev 5430)
+++ sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Http/OSGeo.MapGuide.MaestroAPI.Http.csproj 2010-12-02 09:59:15 UTC (rev 5431)
@@ -46,6 +46,7 @@
<Compile Include="HttpCoordinateSystemCatalog.cs" />
<Compile Include="HttpCoordinateSystemCategory.cs" />
<Compile Include="HttpServerConnection.cs" />
+ <Compile Include="Commands\HttpGetResourceContents.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RequestBuilder.cs" />
<Compile Include="XmlAggregateSetReader.cs" />
Added: sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Native/Commands/LocalGetResourceContents.cs
===================================================================
--- sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Native/Commands/LocalGetResourceContents.cs (rev 0)
+++ sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Native/Commands/LocalGetResourceContents.cs 2010-12-02 09:59:15 UTC (rev 5431)
@@ -0,0 +1,94 @@
+#region Disclaimer / License
+// Copyright (C) 2010, Jackie Ng
+// http://trac.osgeo.org/mapguide/wiki/maestro, jumpinjackie at gmail.com
+//
+// 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.Collections.Generic;
+using System.Text;
+using OSGeo.MapGuide.MaestroAPI.Commands;
+using OSGeo.MapGuide.MaestroAPI.Resource;
+using System.Diagnostics;
+using System.IO;
+
+namespace OSGeo.MapGuide.MaestroAPI.Native.Commands
+{
+ public class LocalGetResourceContents : IGetResourceContents
+ {
+ private MgResourceService _resSvc;
+
+ public LocalGetResourceContents(LocalNativeConnection conn)
+ {
+ this.Parent = conn;
+ _resSvc = (MgResourceService)conn.Connection.CreateService(MgServiceType.ResourceService);
+ }
+
+ Dictionary<string, IResource> IGetResourceContents.Execute(IEnumerable<string> resourceIds)
+ {
+ //There is an implicit assumption here that all resource ids check out and that
+ //there is no duplicates
+
+ var resources = new Dictionary<string, IResource>();
+ if (this.Parent.SiteVersion >= new Version(2, 2))
+ {
+ Trace.TraceInformation("[GetResources]: Using optimal code path provided by 2.2 Resource Service APIs");
+
+ MgStringCollection ids = new MgStringCollection();
+ foreach (var rid in resourceIds)
+ {
+ ids.Add(rid);
+ }
+ //Use the magic of reflection to call newer APIs even though we're referencing
+ //and older assembly
+ System.Reflection.MethodInfo mi = _resSvc.GetType().GetMethod("GetResourceContents");
+ MgStringCollection result = (MgStringCollection)mi.Invoke(_resSvc, new object[] { ids, null });
+
+ int rcount = ids.GetCount();
+ for (int i = 0; i < rcount; i++)
+ {
+ var resId = ids.GetItem(i);
+ using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(result.GetItem(i))))
+ {
+ ResourceTypes resType = ResourceIdentifier.GetResourceType(resId);
+
+ IResource r = ResourceTypeRegistry.Deserialize(resType, ms);
+ r.CurrentConnection = this.Parent;
+ r.ResourceID = resId;
+ resources.Add(resId, r);
+ }
+ }
+ }
+ else
+ {
+ //TODO: Maybe use a ThreadPool in conjunction with cloned connections?
+ Trace.TraceInformation("[GetResources]: Using non-optimal code path provided by pre-2.2 Resource Service APIs");
+ foreach (var rid in resourceIds)
+ {
+ resources.Add(rid, this.Parent.ResourceService.GetResource(rid));
+ }
+ }
+
+ return resources;
+ }
+
+ public IServerConnection Parent
+ {
+ get;
+ private set;
+ }
+ }
+}
Modified: sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Native/LocalNativeCapabilities.cs
===================================================================
--- sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Native/LocalNativeCapabilities.cs 2010-12-02 06:45:19 UTC (rev 5430)
+++ sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Native/LocalNativeCapabilities.cs 2010-12-02 09:59:15 UTC (rev 5431)
@@ -22,6 +22,7 @@
using System.Text;
using OSGeo.MapGuide.MaestroAPI.Services;
using OSGeo.MapGuide.MaestroAPI.Capability;
+using OSGeo.MapGuide.MaestroAPI.Commands;
namespace OSGeo.MapGuide.MaestroAPI.Native
{
@@ -33,10 +34,21 @@
{
get
{
- //TODO: Work out what this can/can't do
- return new int[]
+ if (_parent.SiteVersion >= new Version(2, 2))
{
- };
+ //TODO: Work out what this can/can't do
+ return new int[]
+ {
+ (int)CommandType.GetResourceContents
+ };
+ }
+ else
+ {
+ //TODO: Work out what this can/can't do
+ return new int[]
+ {
+ };
+ }
}
}
Modified: sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Native/LocalNativeConnection.cs
===================================================================
--- sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Native/LocalNativeConnection.cs 2010-12-02 06:45:19 UTC (rev 5430)
+++ sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Native/LocalNativeConnection.cs 2010-12-02 09:59:15 UTC (rev 5431)
@@ -32,6 +32,9 @@
using OSGeo.MapGuide.ObjectModels.Common;
using OSGeo.MapGuide.ObjectModels.MapDefinition;
using OSGeo.MapGuide.MaestroAPI.Exceptions;
+using System.Diagnostics;
+using OSGeo.MapGuide.MaestroAPI.Commands;
+using OSGeo.MapGuide.MaestroAPI.Native.Commands;
namespace OSGeo.MapGuide.MaestroAPI.Native
{
@@ -119,7 +122,7 @@
/// <summary>
/// Returns a working copy of the site connection.
/// </summary>
- private MgSiteConnection Connection
+ internal MgSiteConnection Connection
{
get
{
@@ -578,6 +581,22 @@
return Utility.MgStreamToNetStream(rnd, rnd.GetType().GetMethod("RenderMap", types), args);
}
+ public override Stream RenderDynamicOverlay(RuntimeMap map, MapSelection selection, string format, bool keepSelection)
+ {
+ MgRenderingService rnd = this.Connection.CreateService(MgServiceType.RenderingService) as MgRenderingService;
+ MgResourceService res = this.Connection.CreateService(MgServiceType.ResourceService) as MgResourceService;
+
+ MgMap mmap = new MgMap();
+ mmap.Open(res, map.Name);
+ MgSelection sel = new MgSelection(mmap);
+ if (selection != null)
+ sel.FromXml(selection.ToXml());
+
+ object[] args = new object[] { mmap, sel, format, keepSelection };
+ Type[] types = new Type[] { args[0].GetType(), args[1].GetType(), args[2].GetType(), args[3].GetType() };
+ return Utility.MgStreamToNetStream(rnd, rnd.GetType().GetMethod("RenderDynamicOverlay", types), args);
+ }
+
public override bool IsSessionExpiredException(Exception ex)
{
return ex != null && ex.GetType() == typeof(OSGeo.MapGuide.MgSessionExpiredException) || ex.GetType() == typeof(OSGeo.MapGuide.MgSessionNotFoundException);
@@ -805,9 +824,12 @@
get { return this; }
}
- public OSGeo.MapGuide.MaestroAPI.Commands.ICommand CreateCommand(int commandType)
+ public override OSGeo.MapGuide.MaestroAPI.Commands.ICommand CreateCommand(int cmdType)
{
- throw new NotImplementedException();
+ CommandType ct = (CommandType)cmdType;
+ if (ct == CommandType.GetResourceContents)
+ return new LocalGetResourceContents(this);
+ return base.CreateCommand(cmdType);
}
private IConnectionCapabilities _caps;
@@ -1081,10 +1103,5 @@
var dwSvc = (MgDrawingService)this.Connection.CreateService(MgServiceType.DrawingService);
return Utility.MgStreamToNetStream(dwSvc, dwSvc.GetType().GetMethod("GetSectionResource"), new object[] { new MgResourceIdentifier(resourceID), resourceName });
}
-
- public override Stream RenderDynamicOverlay(RuntimeMap map, MapSelection selection, string format, bool keepSelection)
- {
- throw new NotImplementedException();
- }
}
}
Modified: sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Native/OSGeo.MapGuide.MaestroAPI.Native.csproj
===================================================================
--- sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Native/OSGeo.MapGuide.MaestroAPI.Native.csproj 2010-12-02 06:45:19 UTC (rev 5430)
+++ sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Native/OSGeo.MapGuide.MaestroAPI.Native.csproj 2010-12-02 09:59:15 UTC (rev 5431)
@@ -52,6 +52,7 @@
<Compile Include="..\Properties\GlobalAssemblyInfo.cs">
<Link>GlobalAssemblyInfo.cs</Link>
</Compile>
+ <Compile Include="Commands\LocalGetResourceContents.cs" />
<Compile Include="MgReadOnlyStream.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="LocalNativeCapabilities.cs" />
@@ -68,9 +69,6 @@
<Name>OSGeo.MapGuide.MaestroAPI</Name>
</ProjectReference>
</ItemGroup>
- <ItemGroup>
- <Folder Include="Commands\" />
- </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
More information about the mapguide-commits
mailing list