I discovered the string value “[#ColumnName#]” by browsing the forums, which places the value in the featuresource’s column for a particular feature in its place. I used this for setting the Text property of a WebImage belonging to markers in an InMemoryMarkerOverlay and I believe I have seen it first used for setting the Label of a InMemoryFeatureLayer. This seems incredibly powerful and useful.
Why is this not documented anywhere? I have looked at the documentation and I have not seen any mention of this anywhere.
Where else can this be used, and are there any limitations?
"[#ColumnName#]" String
Hi Michael,
I am sorry our documentation is not enough to describe our APIs now, but we are keeping update it.
And you mentioned the “[#ColumnName#]”, does that meant the column name for the data which is saved in feature’s columnValues?
InMemoryFeatureLayer layer = new InMemoryFeatureLayer();
layer.Columns.Add(new FeatureSourceColumn(“ColumnName”));
If I misunderstand please let me know.
Regards,
Don
Sorry for the delay in getting back to this, I was busy and forgot about it.And you mentioned the “[#ColumnName#]”, does that meant the column name for the data which is saved in feature’s columnValues?
Yes.
I used the string “[#ColumnName#]” to populate the Text property of a WebImage, so that it was set to the the value stored in the specified column in the FeatureSources.
What kinds of object properties can I use this with? Does this work with any string type property , or is its use limited to properties that relate directly to the HTML generated for the object?
Also, does this work for all objects, or only certain objects.
Hi Michael,
Just like you seen, InMemoryMarkerOverlay and InMemoryFeatureLayer is both work in server side, in our logic, overlay contains layers, featurelayer own its featureSource, featureSource contains many features, each feature have its own column values, you can save any value here but it have to be string.
So if you want to use it, you can only read a string, if you want to save any special type, you need write your own logic to “know” what’s its type when read then convert it.
I am sorry I have a little confused about the “object” you mentioned in your question, could you please describe your question more detail? And could you please let me know how you want to use column value for feature in your scenario?
Wish that’s helpful.
Regards,
Don
The “object” I was referring to specifically was an instance of a WebImage object belonging to a MarkerClassBreak object’s DefaultMarkerStyle property. This was used with an InMemoryMarkerOverlay. Let me post some of my code, and this might be a bit clearer.
001.
/// <summary>
002.
/// Setup the AVL Overlay using an InMemoryMarkerOverlay.
003.
/// </summary>
004.
/// <param name=“visible”>Boolean indicating whether the Overlay is visible when the map loads.</param>
005.
/// <param name=“visibleInOverlaySwitcher”>Boolean indicating whether the Overlay is visible in the OverlaySwitcher tool on the map.</param>
006.
/// <returns>InMemoryMarkerOverlay for containing AVL data Markers.
007.
protected
InMemoryMarkerOverlay SetupAVLOverlay(
bool
visible,
bool
visibleInOverlaySwitcher)
008.
{
009.
string
overlayID =
“avlOverlay”
;
010.
011.
//Setup the FeatureSourceColumns.
012.
List<FeatureSourceColumn> columns =
new
List<FeatureSourceColumn>();
013.
014.
//Add the columns to the list.
015.
columns.Add(
new
FeatureSourceColumn(
“Id”
));
016.
columns.Add(
new
FeatureSourceColumn(
“RouteId”
));
017.
columns.Add(
new
FeatureSourceColumn(
“TimeStamp”
));
018.
columns.Add(
new
FeatureSourceColumn(
“Longitude”
));
019.
columns.Add(
new
FeatureSourceColumn(
“Latitude”
));
020.
columns.Add(
new
FeatureSourceColumn(
“Speed”
));
021.
columns.Add(
new
FeatureSourceColumn(
“Heading”
));
022.
columns.Add(
new
FeatureSourceColumn(
“Route”
));
023.
columns.Add(
new
FeatureSourceColumn(
“isLate”
));
024.
025.
//Create the InMemoryMarkerOverlay from the FeatureSourceColumns list.
026.
InMemoryMarkerOverlay avlOverlay =
new
InMemoryMarkerOverlay(overlayID, columns);
027.
028.
//Setup styles.
029.
030.
//Create the ClassBreakMarkerStyle.
031.
ClassBreakMarkerStyle cbms =
new
ClassBreakMarkerStyle(
“isLate”
, BreakValueInclusion.IncludeValue);
032.
033.
//Add MarkerClassBreak objects, they need to be added from smallest value to largest value.
034.
cbms.ClassBreaks.Add(SetupOnTimeClassBreak());
//Break Value: 0
035.
cbms.ClassBreaks.Add(SetupLateClassBreak());
//Break Value: 1
036.
037.
//Setup the MarkerStyle on the ZoomLevels.
038.
avlOverlay.ZoomLevelSet.ZoomLevel01.CustomMarkerStyle = cbms;
039.
040.
avlOverlay.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;
041.
042.
//Apply projection to layers, the projection must be opened before this is done and should be closed afterwards.
043.
try
044.
{
045.
WGS84toSphMercator.Open();
046.
047.
avlOverlay.FeatureSource.Projection = WGS84toSphMercator;
048.
}
049.
finally
050.
{
051.
WGS84toSphMercator.Close();
052.
}
053.
054.
//Set visibility, and return the constructed overlay.
055.
avlOverlay.IsVisible = visible;
056.
avlOverlay.IsVisibleInOverlaySwitcher = visibleInOverlaySwitcher;
057.
058.
return
avlOverlay;
059.
}
060.
061.
/// <summary>
062.
/// Setup MarkerClassBreak for OnTime Routes. Break Value: 0
063.
/// </summary>
064.
/// <returns>A MarkerClassBreak object.
065.
protected
MarkerClassBreak SetupOnTimeClassBreak()
066.
{
067.
//Create the WebImage used for the styles.
068.
WebImage wi =
new
WebImage();
069.
070.
//Setup the FontStyle and TextOffset.
071.
wi.FontStyle = DefaultFont;
072.
wi.TextOffsetX = 20;
073.
074.
//Set the Text property to use the value in the FeatureSource’s Route column.
075.
wi.Text =
“[#Route#]”
;
076.
077.
//Offset the image displayed for the marker, this places the point of the teardrop at the location.
078.
wi.ImageOffsetX = -10.5f;
079.
wi.ImageOffsetY = -25f;
080.
081.
//Set the path for this image.
082.
wi.ImageVirtualPath = @
"~/Content/MapIcons/AVLMarkerOnTime.gif"
;
083.
084.
//Create the MarkerClassBreak with the break value.
085.
MarkerClassBreak mcb =
new
MarkerClassBreak(0);
086.
087.
//Set the DefaultMarkerStyle’s WebImage property to our WebImage.
088.
mcb.DefaultMarkerStyle.WebImage = wi;
089.
090.
//Setup the ContextMenu.
091.
ContextMenu menu =
new
ContextMenu();
092.
093.
//Setup the Schedule Maintenance ContextMenuItem and add it to the ContextMenu.
094.
ContextMenuItem schedMaint =
new
ContextMenuItem();
095.
schedMaint.InnerHtml =
“Schedule Maintenance”
;
096.
schedMaint.OnClientClick =
“openScheduleMaintenance”
;
097.
schedMaint.CssClass =
“mapMenu”
;
098.
schedMaint.HoverCssClass =
“mapMenuHover”
;
099.
menu.MenuItems.Add(schedMaint);
100.
101.
//Setup the Extended AVL Info ContextMenuItem and add it to the ContextMenu.
102.
ContextMenuItem extendAVLInfo =
new
ContextMenuItem();
103.
extendAVLInfo.InnerHtml =
“Info”
;
104.
extendAVLInfo.OnClientClick =
“getExtendedAVLInfo”
;
105.
extendAVLInfo.CssClass =
“mapMenuLastItem”
;
106.
extendAVLInfo.HoverCssClass =
“mapMenuHover”
;
107.
menu.MenuItems.Add(extendAVLInfo);
108.
109.
//Set the ContextMenu width.
110.
menu.Width = 150;
111.
112.
//Set the DefaultMarkStyle.ContextMenu of the MarkerClassBreak to the ContextMenu.
113.
mcb.DefaultMarkerStyle.ContextMenu = menu;
114.
115.
return
mcb;
116.
}
117.
118.
/// <summary>
119.
/// Populates the maps AVL data layer.
120.
/// </summary>
121.
/// <param name=“map”>The ThinkGeo Map object.</param>
122.
/// <param name=“data”>List of datapoints to populate the overlay.</param>
123.
protected
RectangleShape PopulateMapAVLOverlay(Map map, List<vAvlMapData> data)
124.
{
125.
RectangleShape newExtent;
126.
127.
//Get the overlay from the map.
128.
InMemoryMarkerOverlay avlOverlay = (InMemoryMarkerOverlay)map.CustomOverlays[avlOverlayID];
129.
130.
//Clear the InternalFeatures of the overlay. Note we do not need to open and close the FeatureSource when using the InternalFeatures property of the InMemoryFeatureSource.
131.
avlOverlay.FeatureSource.InternalFeatures.Clear();
132.
133.
//Create features from data and add to the overlay.
134.
if
(data !=
null
&& data.Count > 0)
135.
{
136.
foreach
(vAvlMapData datum
in
data)
137.
{
138.
Dictionary<
string
,
string
> dict =
new
Dictionary<
string
,
string
>();
139.
140.
//AVL data.
141.
dict.Add(
“Id”
, datum.avlId.ToString());
142.
dict.Add(
“RouteId”
, datum.avlrteId.ToString());
143.
dict.Add(
“TimeStamp”
, datum.avlTimeStamp.ToString());
144.
dict.Add(
“Longitude”
, datum.avlLongitude.ToString());
145.
dict.Add(
“Latitude”
, datum.avlLatitude.ToString());
146.
dict.Add(
“Speed”
, datum.avlSpeed.ToString());
147.
dict.Add(
“Heading”
, datum.avlHeading.ToString());
148.
dict.Add(
“Route”
, datum.rteName.ToString());
149.
150.
if
(SPs.GetAVLIsLate(datum.avlrteId))
151.
{
152.
dict.Add(
“isLate”
,
“1”
);
153.
}
154.
else
155.
{
156.
dict.Add(
“isLate”
,
“0”
);
157.
}
158.
159.
//Create a PointShape for the Features position.
160.
PointShape position = MappingUtility.PointShapeFromDBCoords(datum.avlLatitude, datum.avlLongitude);
161.
162.
163.
//Create the Feature and add it the overlay’s InternalFeatures.
164.
Feature newFeature =
new
Feature(
new
Vertex(position), datum.avlId.ToString(), dict);
165.
166.
avlOverlay.FeatureSource.InternalFeatures.Add(newFeature);
167.
}
168.
}
169.
170.
//Get the bounding box for the Overlay’s Features.
171.
if
(avlOverlay.FeatureSource.InternalFeatures.Count > 0)
172.
{
173.
try
174.
{
175.
//The FeatureSource must be opened before the GetBoundingBox method is called, and needs to be closed afterwards.
176.
avlOverlay.FeatureSource.Open();
177.
178.
newExtent = avlOverlay.FeatureSource.GetBoundingBox();
179.
}
180.
finally
181.
{
182.
avlOverlay.FeatureSource.Close();
183.
}
184.
}
185.
else
186.
{
187.
//No data in FeatureSource, so use the projected default extent.
188.
newExtent = MappingUtility.GetProjectedDefaultExtent(MappingUtility.WGS84toSphMercator);
189.
}
190.
191.
//Return new map extent
192.
return
newExtent;
193.
}
You can see where I setup the Overlay, define a ClassBreakMarkerStyle, and actually populate the FeatureSource with data. The MarkerClassBreak has a property called DefaultMakerStyle, which has its own property WebImage. The WebImage has a property Text, which I presume relates directly to the markup generated for the marker. When I set the WebImage’s Text property to “[#Route#]” it takes the value from the specified column of the Feature.
If I were to extend the WebImage class, by creating a class called MyWebImage which inherited from WebImage, and added a string property “Message” to it, would I be able to simply use the statement myWebImageInstance.Message = “[#Route#]” and have it receive the value from the specified column of the Feature, or would I have to manually set it in the “set” accessor of the Message property by first parsing out the column name, lookingup the value for that column in the Feature, and then assigning the value? Something like:
public
class
MyWebImage : WebIMage
{
//Members
private
string
message;
public
string
Message
{
get
{
return
message;
}
set
{
string
temp=
""
;
//logic for getting the value from the column and assinging it to temp here
message = temp;
}
}
}
This was just a general question, not really related to anything particular I had planned on implementing at the moment.