How to process layer templates using mapscript to respond to a WMS GetFeatureInfo request
I'm trying to handle GetFeaturInfo WMS requests using c# mapscript. Prior to using mapscript our software passed WMS requests through to a CGI mapserver hosted on IIS. This processed a html template associated with each queried layer and substituted a number of tokens within the template for data.
We cannot use the mapserver cgi implementation so I'm attempting to reimplement this mechanism using mapscript via the C# mapscript mechanism.
The summary of the code I have so far is here. The problem with this is that the call to processQueryTemplate causes an AccessViolation Exception to be thrown.
public string GetFeatureInfoFromWMS(NameValueCollection WMSqueryString)
{
//Set the projection library environment variable used by mapscript.dll
Environment.SetEnvironmentVariable("PROJ_LIB", ProjectionLibraryPath);
string output = string.Empty;
try
{
using (mapObj map = new mapObj(MapFile))
{
//Add aditional layer specific params - ie location of plugins etc
ProcessLayers(map, WMSqueryString);
map.web.metadata.set("wms_onlineresource", WMSOnlineResourceURL);
string xVal = WMSqueryString["X"];
string yVal = WMSqueryString["Y"];
if (xVal == null || yVal == null)
{
throw new ArgumentNullException("The X or Y point value has not been suppplied in the GetFeatureInfo request");
}
double pointX = 0.0;
double pointY = 0.0;
try
{
pointX = Convert.ToDouble(xVal);
pointY = Convert.ToDouble(yVal);
}
catch (Exception e)
{
throw new ArgumentException("The X or Y point value supplied in the GetFeatureInfo request is not a valid decimal",e);
}
string layersQS = WMSqueryString["QUERY_LAYERS"];
if (layersQS == null)
{
throw new ArgumentNullException("The QUERY_LAYERS parameter of the WMS GetFeatureInfo request is not specified correctly");
}
//Load the parameters from the wms request into the map
using (OWSRequest request = new OWSRequest())
{
for (int i = 0; i < WMSqueryString.Count; i++)
{
request.setParameter(WMSqueryString.GetKey(i), WMSqueryString.Get(i));
}
string wmsVersion = WMSqueryString["VERSION"];
if (wmsVersion == null || wmsVersion == string.Empty) wmsVersion = DEFAULT_WMS_VERSION;
map.loadOWSParameters(request, wmsVersion);
}
//Reproject X & Y pixel co-ordinates in map co-ordintes.
double minX = map.extent.minx;
double maxX = map.extent.maxx;
double geoX = minX + ((pointX / (double)map.widt开发者_StackOverflowh) * (maxX - minX));
double minY = map.extent.miny;
double maxY = map.extent.maxy;
double geoY = maxY - ((pointY / (double)map.height) * (maxY - minY));
string[] queryLayers = layersQS.Split(',');
using (pointObj point = new pointObj(geoX, geoY, 0, 0))
{
foreach (string layerName in queryLayers)
{
using (layerObj layer = map.getLayerByName(layerName))
{
int queryResult = layer.queryByPoint(map, point, (int)MS_QUERY_MODE.MS_QUERY_SINGLE, -1);
}
}
}
map.prepareQuery();
string[] names = { "Token1" };
string[] values = { "Value1" };
//BANG!!!!!!
output = map.processQueryTemplate(names, values, 10);
}
return output;
}
catch (Exception ex)
{
throw;
}
}
The associated map file is as follows:
MAP
#
# Start of map file
#
NAME esdm
STATUS ON
TEMPLATEPATTERN "."
SIZE 400 600
UNITS meters
EXTENT 0 0 800000 1200000
IMAGECOLOR 255 255 255
FONTSET fonts.txt
#DEBUG ON
IMAGETYPE PNG
PROJECTION
"init=epsg:27700"
END
OUTPUTFORMAT
NAME "png"
DRIVER "GD/PNG"
IMAGEMODE RGBA
MIMETYPE image/png
EXTENSION png
TRANSPARENT ON
END
# OUTPUTFORMAT
# NAME "imagemap"
# MIMETYPE text/html; driver=imagemap
# DRIVER "imagemap"
# END
#
# Start of web interface definition (including WMS enabling metadata)
#
WEB
METADATA
"wms_title" "SQL mapping data"
"wms_srs" "EPSG:27700 EPSG:4326 EPSG:54004 EPSG:54005 EPSG:900913"
"wms_feature_info_mime_type" "text/plain"
"wms_include_items" "all"
END
END
INCLUDE "mapSymbols.inc"
# BARSActions Point Layer
#-----------------------------------------------------------------------------------------
LAYER
NAME "Actions"
MAXSCALEDENOM 100000000
MINSCALEDENOM 0
METADATA
"wms_title" "BARSActions"
"wfs_title" "BARSActions"
"wms_srs" "EPSG:27700"
END
CONNECTIONTYPE PLUGIN
PLUGIN "SQLPlugin"
DATA "geom from MapLoadTest USING UNIQUE ActionId USING SRID=27700"
FILTER "(OrgUnitId = 1 AND %ActionStatusID% AND %ActionTypeID% AND %AreaIDST(geom)%)"
TYPE POINT
STATUS ON
TOLERANCE 50
TEMPLATE "barsTemplate.htm"
CLASS
COLOR 0 0 255
OUTLINECOLOR 0 0 0
SYMBOL 'star'
SIZE 15
#MAXSIZE 6
#MINSIZE 3
END # end of class object
PROJECTION
"init=epsg:27700"
END
DUMP True
END # end of layer object
# BARSActions Polygon Layer
#-----------------------------------------------------------------------------------------
LAYER
NAME "ActionsPolygons"
MAXSCALEDENOM 100000000
MINSCALEDENOM 0
METADATA
"wms_title" "BARSActionsPolygons"
"wfs_title" "BARSActionsPolygons"
"wms_srs" "EPSG:27700"
END
CONNECTIONTYPE PLUGIN
PLUGIN "SQLPlugin"
DATA "geom from MapLoadTest USING UNIQUE ActionId USING SRID=27700"
FILTER "(OrgUnitId = 2 AND ActionID = 200 AND %ActionStatusID% AND %ActionTypeID% AND %AreaIDST(geom)%)"
TYPE POLYGON
STATUS ON
TOLERANCE 50
TEMPLATE "barsTemplate.htm"
CLASS
COLOR 0 0 255
OUTLINECOLOR 0 0 0
END # end of class object
PROJECTION
"init=epsg:27700"
END
DUMP True
END # end of layer object
END # Map File
Various items in the map file are tokenised (ie the sql plugin location and the filters applied to the data) This is handled by the call to ProcessLayers in the previous method. This mechanism doesn't appear to cause any problems when drawing maps. The call to queryByPoint works. It returns a success and the query run against the sql db returns expected data.
I'm unsure where to proceed from here and what else needs to be done to produce the output from the templates. I was expecting the call to processQueryTemplate to return the populated templetes. I also don't quite know what prepareQuery is supposed to do.
Cheers
Never figured out how to get the templates to work. However I managed to get the database ID's of the shapes returned by the query. I then use these to prepare the HTML result elsewhere.
Note that I'm using map.querybypoint to deal with situations where there are layer groups (in which case the layergroup name will come across as the layer name in the wms request.
/// <summary>
/// Handles a GetFeature info request and returns matching ActionID's.
/// </summary>
/// <param name="WMSqueryString">The WM squery string.</param>
/// <returns>A list of matching action ID's.</returns>
/// <exception cref="ArgumentNullException">Thrown if the X or Y point values are not supplied in the WMS GetFeatureInfo request</exception>
/// <exception cref="ArgumentException">Thrown in the X or Y point values supplied in the WMS GetFeatureInfo request cannot be converted to valid doubles</exception>
public List<int> GetFeatureInfoActionID(NameValueCollection WMSqueryString)
{
//Set the projection library environment variable used by mapscript.dll
Environment.SetEnvironmentVariable("PROJ_LIB", ProjectionLibraryPath);
try
{
List<int> resultsList = new List<int>();
using (mapObj map = new mapObj(MapFile))
{
ProcessLayers(map, WMSqueryString);
map.web.metadata.set("wms_onlineresource", WMSOnlineResourceURL);
//Load the parameters from the wms request into the map
using (OWSRequest request = new OWSRequest())
{
for (int i = 0; i < WMSqueryString.Count; i++)
{
request.setParameter(WMSqueryString.GetKey(i), WMSqueryString.Get(i));
}
string wmsVersion = WMSqueryString["VERSION"];
if (wmsVersion == null || wmsVersion == string.Empty) wmsVersion = DEFAULT_WMS_VERSION;
map.loadOWSParameters(request, wmsVersion);
}
string xVal = WMSqueryString["X"];
string yVal = WMSqueryString["Y"];
if (xVal == null || yVal == null)
{
throw new ArgumentNullException("The X or Y point value has not been suppplied in the GetFeatureInfo request");
}
double pointX = 0.0;
double pointY = 0.0;
try
{
pointX = Convert.ToDouble(xVal);
pointY = Convert.ToDouble(yVal);
}
catch (Exception e)
{
throw new ArgumentException("The X or Y point value supplied in the GetFeatureInfo request is not a valid decimal",e);
}
//Reproject X & Y pixel co-ordinates in map co-ordintes.
double minX = map.extent.minx;
double maxX = map.extent.maxx;
double geoX = minX + ((pointX / (double)map.width) * (maxX - minX));
double minY = map.extent.miny;
double maxY = map.extent.maxy;
double geoY = maxY - ((pointY / (double)map.height) * (maxY - minY));
MS_RETURN_VALUE queryResult;
using (pointObj point = new pointObj(geoX, geoY, 0, 0))
{
queryResult = (MS_RETURN_VALUE)map.queryByPoint(point, (int)MS_QUERY_MODE.MS_QUERY_MULTIPLE, -1);
}
if (queryResult != MS_RETURN_VALUE.MS_SUCCESS)
{
return null;
}
map.prepareQuery();
for (int layerIndex = 0; layerIndex < map.numlayers; layerIndex++)
{
using (layerObj layer = map.getLayer(layerIndex))
{
int resultCount = layer.getNumResults();
if (resultCount > 0)
{
layer.open();
for (int resultIndex = 0; resultIndex < resultCount; resultIndex++)
{
using (resultCacheMemberObj resultCache = layer.getResult(resultIndex))
{
int actionID = resultCache.shapeindex;
if (actionID != 0 && resultsList.Contains(actionID) == false)
{
resultsList.Add(actionID);
}
}
}
layer.close();
}
}
}
}
return resultsList;
}
catch (Exception ex)
{
throw;
}
}
精彩评论