I am attempting to complete the first tutorial in the Azure IoT learning path but every time I try to make the device find a customer it says "Failed to find map route." in the console. I've followed all the steps, looked over the code and there's no troubleshooting answers for this particular error. The device is connected to my program it just cannot find the map route.
static void GetRoute(StateEnum newState)
{
// Set the state to ready, until the new route arrives.
state = StateEnum.ready;
var req = new RouteRequestDirections
{
Query = FormattableString.Invariant($"{currentLat},{currentLon}:{destinationLat},{destinationLon}")
};
var directions = azureMapsServices.GetRouteDirections(req).Result;
if (directions.Error != null || directions.Result == null)
{
// Handle any error.
redMessage("Failed to find map route");
}
else
{
int nPoints = directions.Result.Routes[0].Legs[0].Points.Length;
greenMessage($"Route found. Number of points = {nPoints}");
// Clear the path. Add two points for the start point and destination.
path = new double[nPoints + 2, 2];
int c = 0;
// Start with the current location.
path[c, 0] = currentLat;
path[c, 1] = currentLon;
++c;
// Retrieve the route and push the points onto the array.
for (var n = 0; n < nPoints; n++)
{
var x = directions.Result.Routes[0].Legs[0].Points[n].Latitude;
var y = directions.Result.Routes[0].Legs[0].Points[n].Longitude;
path[c, 0] = x;
path[c, 1] = y;
++c;
}
// Finish with the destination.
path[c, 0] = destinationLat;
path[c, 1] = destinationLon;
// Store the path length and time taken, to calculate the average speed.
var meters = directions.Result.Routes[0].Summary.LengthInMeters;
var seconds = directions.Result.Routes[0].Summary.TravelTimeInSeconds;
var pathSpeed = meters / seconds;
double distanceApartInMeters;
double timeForOneSection;
// Clear the time on the path array. The path array is 1 less than the points array.
timeOnPath = new double[nPoints + 1];
// Calculate how much time is required for each section of the path.
for (var t = 0; t < nPoints + 1; t++)
{
// Calculate distance between the two path points, in meters.
distanceApartInMeters = DistanceInMeters(path[t, 0], path[t, 1], path[t + 1, 0], path[t + 1, 1]);
// Calculate the time for each section of the path.
timeForOneSection = distanceApartInMeters / pathSpeed;
timeOnPath[t] = timeForOneSection;
}
truckOnSection = 0;
truckSectionsCompletedTime = 0;
timeOnCurrentTask = 0;
// Update the state now the route has arrived. Either: enroute or returning.
state = newState;
}
}
For the error make sure that:
The key call within the code above is var directions = azureMapsServices.GetRouteDirections(req).Result;. The directions
structure is complex. Consider setting a breakpoint in this method,
and examining the contents of directions.
You can use the below example code to add the direct method to deliver to a customer :
static Task<MethodResponse> CmdGoToCustomer(MethodRequest methodRequest, object userContext)
{
try
{
// Pick up variables from the request payload, with the name specified in IoT Central.
var payloadString = Encoding.UTF8.GetString(methodRequest.Data);
int customerNumber = Int32.Parse(payloadString);
// Check for a valid key and customer ID.
if (customerNumber >= 0 && customerNumber < customer.Length)
{
switch (state)
{
case StateEnum.dumping:
case StateEnum.loading:
case StateEnum.delivering:
eventText = "Unable to act - " + state;
break;
case StateEnum.ready:
case StateEnum.enroute:
case StateEnum.returning:
if (contents == ContentsEnum.empty)
{
eventText = "Unable to act - empty";
}
else
{
// Set event only when all is good.
eventText = "New customer: " + customerNumber.ToString();
destinationLat = customer[customerNumber, 0];
destinationLon = customer[customerNumber, 1];
// Find route from current position to destination, storing route.
GetRoute(StateEnum.enroute);
}
break;
}
// Acknowledge the direct method call with a 200 success message.
string result = "{\"result\":\"Executed direct method: " + methodRequest.Name + "\"}";
return Task.FromResult(new MethodResponse(Encoding.UTF8.GetBytes(result), 200));
}
else
{
eventText = $"Invalid customer: {customerNumber}";
// Acknowledge the direct method call with a 400 error message.
string result = "{\"result\":\"Invalid customer\"}";
return Task.FromResult(new MethodResponse(Encoding.UTF8.GetBytes(result), 400));
}
}
catch
{
// Acknowledge the direct method call with a 400 error message.
string result = "{\"result\":\"Invalid call\"}";
return Task.FromResult(new MethodResponse(Encoding.UTF8.GetBytes(result), 400));
}
}
For more information please refer this MS TUTORIAL : Create your first Azure IoT Central App & Create a programming project for a real device
MS Q&A: Error - Create your first Azure IoT Central app
Related
I'm making a simple game in C# where the player fights until they have no more health:
public void FightLoop(int currentHealth) {
bool death = false;
do{
hit = false;
if(currentHealth == 0){
death = true;
}
turn = turn + 1;
string turnRecord = turn.ToString();
string firstPlayerChoiceString = GetPlayerInput();
string secondPlayerChoiceString = GetRandomWeapon ();
string gameResult = DetermineWinner(firstPlayerChoiceString, secondPlayerChoiceString, currentHealth, turnRecord);
if(hit == true){
currentHealth = currentHealth - 1;
}
FightRecord();
AddRecord(firstPlayerChoiceString, secondPlayerChoiceString, gameResult, turnRecord, turn);
}while (death == false);
DisplayFightHistory ();
}
At the end of each fight, a summary of the whole thing should be shown (array with turns, attacks and results).
public void FightRecord(int recordSize = 100)
{
try
{
fightRecordSize = recordSize;
fightRecord = new string[fightRecordSize, 4];
}
catch (OverflowException e)
{
System.Console.WriteLine("OverflowException during FightRecord initialization: \"{0}\"\nrecordSize given was [{1}]\nSetting recordSize to 10", e.Message, recordSize);
fightRecordSize = 100;
fightRecord = new string[fightRecordSize, 4];
}
fightRecordCurrentSize = fightRecordCurrentSize++;
}
public void AddRecord(string playerOneChoice, string playerTwoChoice, string gameResult, string turnRecord, int turn)
{
// Insert the record data
fightRecord[fightRecordCurrentIndex, 0] = playerOneChoice;
fightRecord[fightRecordCurrentIndex, 1] = playerTwoChoice;
fightRecord[fightRecordCurrentIndex, 2] = gameResult;
fightRecord[fightRecordCurrentIndex, 3] = turnRecord;
// Increment the fight index counter and current history size
fightRecordCurrentIndex = (fightRecordCurrentIndex + 1) % fightRecordSize;
if (fightRecordCurrentSize < fightRecordSize)
{
fightRecordCurrentSize++;
}
}
public void DisplayFightHistory () {
System.Console.WriteLine ("\nPodsumowanie:");
for (int i = 0; i < fightRecordCurrentSize; i++){
System.Console.WriteLine ("Tura #{0}:\t{1}\t-\t{2},\t{3} {4}",
fightRecord[i,3], fightRecord[i,0], fightRecord[i,1], fightRecord[i,2], i);
}
}
However, every time I get only the last value, like this:
Summary:
Turn #: - ,
Turn #: - ,
Turn #: - ,
Turn #: - ,
Turn #: - ,
Turn #: - ,
Turn #8: Sword - Spear, You lose
By following the whole process of creating the array, it seems like the values are being put in it correctly at first, but with each new turn, only the last value remains.
Does anyone have any idea why it's happening?
Use the loop variable as the index.
Change
fightRecord[displayRecordIndex,3], fightRecord[displayRecordIndex,0], fightRecord[displayRecordIndex,1], fightRecord[displayRecordIndex,2], displayRecordIndex);
To
fightRecord[i,3], fightRecord[i,0], fightRecord[i,1], fightRecord[i,2], i);
this is my very first post here on StackOverflow so please tell me if I did anything wrong, also english is not my native language, forgive me if there is any gramatical errors.
My question is how can I permutate the items of an array of type "Location", I need to get all possible permutations of waypoints given by the user to then calculate the best route based on time or distance. (I don't want to use the normal route calculation)
I've searched for algorithms but all of them when I put the array of type "Location[]" in the function's parameter I get the error that the object needs to be IEnumerable and I don't know how to convert to that if is even possible, I never worked with IEnumerable.
If it is of any help this is my code for calculating the route:
//Gets the waypoints from a listBox provided by the user, "mode" selects between best time and best distance
//backgroundworker so the UI dont freezes, and return the optimal waypoint order
public Location[] CalcularRota(Location[] waypoints, int mode, BackgroundWorker work, DoWorkEventArgs e)
{
//Declarations
string origem = "";
string destino = "";
Rota[] prop = new Rota[100]; //this index is the number of times the algorithm will be executed, more equals accuracy but much more time to complete
Rota bestDist = new Rota();
Rota bestTime = new Rota();
DirectionService serv = new DirectionService();
DirectionRequest reqs = new DirectionRequest();
DirectionResponse resp;
Random rnd = new Random();
Location[] rndWays;
int dist = 0;
int ti = 0;
bestDist.Distance = 1000000000; //put higher values for the first comparation to be true (end of code)
bestTime.Time = 1000000000;
if (waypoints != null)
{
reqs.Sensor = false;
reqs.Mode = TravelMode.driving;
for (int i = 0; i < prop.Length; i++) //initializes prop
prop[i] = new Rota();
for (int i = 0; i < prop.Length; i++)
{
rndWays = waypoints.OrderBy(x => rnd.Next()).ToArray(); //randomizes the order, I want to get all permutations and then test
//but I dont know how so I've been using randomized
dist = ti = 0;
origem = prop[0].ToString(); //save this particular waypoint's origin and destination
destino = prop[1].ToString();
reqs.Origin = origem;
reqs.Destination = destino;
if (waypoints.Length > 0)
reqs.Waypoints = rndWays;
resp = serv.GetResponse(reqs); //request the route with X order of waypoints to google
if (resp.Status == ServiceResponseStatus.Ok) //wait the response otherwise the program crashes
{
for (int j = 0; j < resp.Routes[0].Legs.Length; j++) //gets the distance and time of this particular order
{
ti += int.Parse(resp.Routes[0].Legs[j].Duration.Value);
dist += int.Parse(resp.Routes[0].Legs[j].Distance.Value);
}
}
prop[i].Origem = origem; //saves this waypoints order details for further comparison
prop[i].Destino = destino;
prop[i].Distance = dist;
prop[i].Time = ti;
prop[i].Order = rndWays;
work.ReportProgress(i); //report the progress
}
for (int i = 0; i < prop.Length; i++) //gets the best distance and time
{
if (bestDist.Distance > prop[i].Distance)
{
bestDist.Distance = prop[i].Distance;
bestDist.Time = prop[i].Time;
bestDist.Order = prop[i].Order;
bestDist.Origem = prop[i].Origem;
bestDist.Destino = prop[i].Destino;
}
if (bestTime.Time > prop[i].Time)
{
bestTime.Distance = prop[i].Distance;
bestTime.Time = prop[i].Time;
bestTime.Order = prop[i].Order;
bestTime.Origem = prop[i].Origem;
bestTime.Destino = prop[i].Destino;
}
}
if (bestDist.Order == bestTime.Order) //if the same waypoint order has the same time and distance
return bestDist.Order; // returns whatever bestDist.Order or bestTime.Order
else if (bestDist.Order != bestTime.Order) //if different returns corresponding to the mode selected
{
if (mode == 1) return bestDist.Order;
if (mode == 2) return bestTime.Order;
}
}
return null;
}
What I want is to permutate the waypoints given and test each permutation, I've been struggling with this for a time, if u guys could help me with any way possible would be great.
Ty.
EDIT.
I found this function here on StackOverflow:
public static bool NextPermutation<T>(T[] elements) where T : IComparable<T>
{
var count = elements.Length;
var done = true;
for (var i = count - 1; i > 0; i--)
{
var curr = elements[i];
// Check if the current element is less than the one before it
if (curr.CompareTo(elements[i - 1]) < 0)
{
continue;
}
// An element bigger than the one before it has been found,
// so this isn't the last lexicographic permutation.
done = false;
// Save the previous (bigger) element in a variable for more efficiency.
var prev = elements[i - 1];
// Have a variable to hold the index of the element to swap
// with the previous element (the to-swap element would be
// the smallest element that comes after the previous element
// and is bigger than the previous element), initializing it
// as the current index of the current item (curr).
var currIndex = i;
// Go through the array from the element after the current one to last
for (var j = i + 1; j < count; j++)
{
// Save into variable for more efficiency
var tmp = elements[j];
// Check if tmp suits the "next swap" conditions:
// Smallest, but bigger than the "prev" element
if (tmp.CompareTo(curr) < 0 && tmp.CompareTo(prev) > 0)
{
curr = tmp;
currIndex = j;
}
}
// Swap the "prev" with the new "curr" (the swap-with element)
elements[currIndex] = prev;
elements[i - 1] = curr;
// Reverse the order of the tail, in order to reset it's lexicographic order
for (var j = count - 1; j > i; j--, i++)
{
var tmp = elements[j];
elements[j] = elements[i];
elements[i] = tmp;
}
// Break since we have got the next permutation
// The reason to have all the logic inside the loop is
// to prevent the need of an extra variable indicating "i" when
// the next needed swap is found (moving "i" outside the loop is a
// bad practice, and isn't very readable, so I preferred not doing
// that as well).
break;
}
// Return whether this has been the last lexicographic permutation.
return done;
}
The usage is:
NextPermutation(array);
Doing this and putting my array (rndWays) as overload I get the following error:
The type 'Google.Maps.Location' cannot be used as type parameter 'T' in the generic type or method 'Form1.NextPermutation< T >(T[])'. There is no implicit reference conversion from 'Google.Maps.Location' to 'System.IComparable< Google.Maps.Location >'.
The problem is that Location does not implement the IComparable interface.
Change:
public static bool NextPermutation<T>(T[] elements) where T : IComparable<T>
to:
public static bool NextPermutation(Location[] elements)
And replace each CompareTo() with your own comparison function.
I am having trouble with a C# script that uses the Sony Vegas Pro scripting API to generate an edit list of video clips for trimming in ffmpeg. The full script and details of the purpose can be found at http://www.sonycreativesoftware.com/forums/showmessage.asp?forumid=21&messageid=932542.
Part of my script is shown below. When I run the script I get the error "The name 'OffsetAdjusterFrames' does not exist in the current context", relating to the line Timecode OffsetAdjuster = Timecode.FromFrames(OffsetAdjusterFrames); , and "The name 'DurationAdjusterFrames' does not exist in the current context" relating to the next line.
The problem seems to be with the line if (clipOffsetOriginalFrames == 0) or with something else in that if else if else statement. If I bypass the whole of that if else if else statement by commenting it out and explicitly declaring OffsetAdjusterFrames and DurationAdjusterFrames then the rest of the script works.
Any help would be appreciated as I have run out of things to try. Thanks.
Edit: This is different to enter link description here because this is C# and that is Java, so it's not really a duplicate, but the general solution is the same.
...
Timecode clipOffsetOriginal = videoEvent.ActiveTake.Offset;
// clipOffsetOriginal as a number of frames
Int64 clipOffsetOriginalFrames = videoEvent.ActiveTake.Offset.FrameCount;
Timecode clipOffset = clipOffsetOriginal - startAdd;
Timecode clipDuration = videoEvent.Length + startAdd + endAdd;
// Reset start to zero if start was not trimmed at all, and compensate length
if (clipOffsetOriginalFrames == 0)
{
int OffsetAdjusterFrames = 2;
int DurationAdjusterFrames = -2;
}
// Reset start to zero if start had been trimmed by just 1 frame, and compensate length
else if (clipOffsetOriginalFrames == 1)
{
int OffsetAdjusterFrames = 1;
int DurationAdjusterFrames = -1;
}
else
{
int OffsetAdjusterFrames = 0;
int DurationAdjusterFrames = 0;
}
Timecode OffsetAdjuster = Timecode.FromFrames(OffsetAdjusterFrames);
Timecode DurationAdjuster = Timecode.FromFrames(DurationAdjusterFrames);
Timecode clipOffsetAdjusted = clipOffset + OffsetAdjuster;
Timecode clipDurationAdjusted = clipDuration + DurationAdjuster;
// Convert start and duration from timecode to seconds
double start = clipOffsetAdjusted.ToMilliseconds() / 1000;
double duration = clipDurationAdjusted.ToMilliseconds() / 1000;
string triminfo = String.Format(basename + ".mp4 " + start + " " + duration);
...
The variables OffsetAdjusterFrames and DurationAdjusterFrames are declared inside the if statement, so they are local variables and can't be called from outside your if statements.
When you move the declaration outside of the if statement, say into the method, it works because the scope of the variable is in the method, so can be called outside of the if statement as long as it's within the method.
So basically just declare the variables at the start of the method, like this:
int OffsetAdjusterFrames;
int DurationAdjusterFrames;
and then assign the values in your if statements like this:
// Reset start to zero if start was not trimmed at all, and compensate length
if (clipOffsetOriginalFrames == 0)
{
OffsetAdjusterFrames = 2;
DurationAdjusterFrames = -2;
}
// Reset start to zero if start had been trimmed by just 1 frame, and compensate length
else if (clipOffsetOriginalFrames == 1)
{
OffsetAdjusterFrames = 1;
DurationAdjusterFrames = -1;
}
else
{
OffsetAdjusterFrames = 0;
DurationAdjusterFrames = 0;
}
I am using Windows 8's media capture class to click a photo in a desktop application and copy it to clipboard.
My function takes two inputs as argument,
1) the desired device (front, back or usb web cam) and
2) the desired resolution
Here is the function:
async public void UseCamera(int x, int y)
{
MediaCapture _mediaCapture = new MediaCapture();
var _ImageFormat = ImageEncodingProperties.CreatePng();
var _fileStream = new InMemoryRandomAccessStream();
MediaCaptureInitializationSettings _cameraSettings1 = new MediaCaptureInitializationSettings();
DeviceInformationCollection _deviceInformationCollection = null;
IReadOnlyList<IMediaEncodingProperties> res;
_deviceInformationCollection = await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture);
if (x > _deviceInformationCollection.Count - 1)
{
MessageBox.Show("Device Not found");
}
else
{
_cameraSettings1.VideoDeviceId = _deviceInformationCollection[x].Id;
_cameraSettings1.AudioDeviceId = "";
_cameraSettings1.PhotoCaptureSource = PhotoCaptureSource.VideoPreview;
_cameraSettings1.StreamingCaptureMode = StreamingCaptureMode.Video;
await _mediaCapture.InitializeAsync(_cameraSettings1);
res = _mediaCapture.VideoDeviceController.GetAvailableMediaStreamProperties(MediaStreamType.VideoPreview);
uint maxResolution = 0;
List<int> indexMaxResolution = new List<int>();
if (res.Count >= 1)
{
for (int i = 0; i < res.Count; i++)
{
VideoEncodingProperties vp = (VideoEncodingProperties)res[i];
if (vp.Width > maxResolution)
{
indexMaxResolution.Add(i);
maxResolution = vp.Width;
}
}
indexMaxResolution.Reverse();
if (y > indexMaxResolution.Count())
{
MessageBox.Show("Maximum supported resolution index : " + (indexMaxResolution.Count - 1).ToString());
}
//this is the part that I believe is the trouble maker
else
{
await _mediaCapture.VideoDeviceController.SetMediaStreamPropertiesAsync(MediaStreamType.VideoPreview, res[indexMaxResolution.ElementAt(y)]);
await _mediaCapture.CapturePhotoToStreamAsync(_ImageFormat, _fileStream);
Clipboard.SetImage(Image.FromStream(_fileStream.AsStream()));
}
}
}
}
The function is working, but the problem is that it is incredibly slow.. it takes almost 4-5 seconds to capture a photo. Can anyone tell me where am i going wrong and how can I speed things up. Because I tested my camera, and it can take clicks # almost 2 pics per second..
If you move all the initialization and device information queries to an initialization function you might see an increase in speed.
In my experience gathering device information is slow.
Try to do as much as possible upfront so that when the time comes to capture, only the necessary things need to be done.
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.width) * (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;
}
}