GIS Programming Makes You Lazy
My friend just responded to me with an email quoting Annie Lennox "Would I lie to you?" Apparently, the answer is yes. I said we were done with the previous series that created a tool, but I thought about it and decided it is the perfect opportunity to show a few more features. So while technically the tool was complete, we can still build upon the code we created. By now, you might be questioning what the value of the tool was. None really. I mean, you never know, maybe someone was looking for exactly that, and I think that is why ESRI exposed all the classes for the public to use. They now they can't create a tool to do everything (sometimes they do), and so they give people abundant access to create their own. Look at how many ArcScripts there are available to download - thousands! The point of the tool wasn't to presuppose that it would be of specific use to you, but all the pieces that make it up are valuable bits of code. If you break it up you now know how to: get the x, y coordinates of a user click, get the feature and select a feature, create a shapefile, add fields to a shapefile, and add values to a feature class including geometry and attributes. The different pieces are what you will probably use over and over again.
To expand the tool - wouldn't it be useful to add the newly created shapefile? We already know what type of interface to use when retrieving a feature layer as opposed to a raster layer or annotation layer. It is IFeatureLayer. Since we want to add a new layer, you can probably guess that we need to create a new variable. Let's start by creating a sub routine with one argument that accepts a featureclass called AddTheFC
Private Sub AddTheFC (ByVal pFeatClass as IFeatureClass)
And then create a new FeatureLayer variable.
Dim pFeatLayer as IFeatureLayer
Set pFeatLayer = New FeatureLayer
We're basically working backwards from how we get a featureclass. We have the featureclass now we need to set it.
Set pFeatLayer.FeatureClass = pFeatClass
We should also give it a name. We'll use the featureclass property AliasName. By default this is the same name as the featureclass, but can be set to something different. Since we didn't set it to anything, it should be the name that we created in the CreateShapefile function.
pFeatLayer.Name = pFeatClass.AliasName
Now, can you guess how to add the layer. To get the layer we go through the focusmap, or the IMap interface, so it should be natural that we also add new layers through the IMap interface. Since we have already set this up in the starting sub routing (InPoint), then we should just pass this as an argument. Modify the AddTheFC sub to have a second argument that passes the pMap variable.
Private Sub AddTheFC(ByVal pFeatClass As IFeatureClass, ByVal pMap as IMap)
Just a note here. I have been using the same variable name. Keep in mind, though, these are not the same variables. They are separate. You can just as easily call them by another name.
Now if we look at the IMap Interface. There is a method called AddLayer. It only takes one argument of the type ILayer. We know that IFeatureLayer implements ILayer so we can just pass the method our pFeatLayer variable.
At the end of the InPoint Method, after the pNewFeat.Store line of code. Add a line of code to call the new sub.
AddTheFC pNewFC, pMap
And before we do pMxDoc.ActiveView.Refresh, we need to update the table of contents.
Try it out. Should work for you. Don't forget to select the layer (I did).
In the further reading section there is an example of how to open a shapefile from the local computer and add it as a layer.
That's all well and good, but we are stuck using the default symbology. Say we wanted to have a specific symbology set up for when we add our layer.
Our interest is in the single symbol option if you were to manually change the symbol of the layer. There are other options to classify the layer, but this is overkill for a layer with only one feature.
To do this we will use what are called Renderers. Renderers control the style of the layers in the table of contents. For the single symbol option we will use the ISimpleRenderer interface. If we look at the actual interface page, we find an example. It is pretty sparse, but lets us know all the other interfaces we need to work with.
The simple renderer only takes one type of symbol that will be applied to all the features. If we used a renderer for, say categories, then we would use different symbols for each category. Symbols come in a variety of flavors, one for each of the geographic primitives (e.g. markers (points), polylines, and polygons). A FillSymbol is one such symbol, used for handing the symbology of a polygon. A polygon is really an enclosed polyline, hence the term fill. So underneath the
pFeatLayer.Name = pFeatClass.AliasName
line lets declare two new variables:
Dim pFillSymb as IFillSymbol
Dim pSimpRend as ISimpleRenderer
There are a number of FillSymbols that we can use when creating our new FillSymbol. We are interested in just a simple fill symbol, but we could use something like a picture or marker fill symbol if we wanted to be more adventurous.
Set pFillSymb = New SimpleFillSymbol
If we look at the properties for IFillSymbol we can see it accepts to properties: Color and Outline. Outline is yet another symbol type that accepts an ILineSymbol. We won't deal with that, but it is an area that you could play around with once the lesson is done, because with the LineSymbol you can set the outline color and width. The Color property accepts an IColor object. There are different ways to do this, but we'll start by declaring a variable:
Dim pColor as IColor
Again, when we create a new IColor we have different options as to what it could be. I think most people are familiar with RGB, Red Green Blue, colors. Basically, the way this works is by combining these three primary colors you get whole new colors. You only use a range of numbers for each color, 0-255. If each color has a value of 255, then the resultant color is white, and if each color has a value of 0, then the resultant color is black. There are other options, for example CMYK (Cyan Magenta Yellow Black). CMYK is commonly used with printers/plotters, so you might also be familiar with that. I recomend checking out ColorBrewer for information on picking out color schemes. Let's stick with RGB though.
Set pColor = New RGBColor
Next we can pick the different values for red, green, and blue. Remember this isn't the outline, but the fill color. We can use the RGB property and a built-in function called, surprise, RGB. This passes the three values as an RGB color to the property.
pColor.RGB = RGB(30, 144, 255)
Now we can set the color property of the fillsymbol.
pFillSymb.Color = pColor
Before we can use our fill symbol we need to create a new simple renderer. We already have the simple renderer variable:
Set pSimpRend = New SimpleRenderer
Next we can set the symbol for the renderer. In this case we need to use the Set key word.
Set pSimpRend.Symbol = pFillSymbol
We'll throw in a few other properties while we're at it.
pSimpRend.Label = "Example"
pSimpRend.Description = "Description"
Now if you've sneeked a peak at the properties of a FeatureLayer you might have noticed there is now symbol property. We actually need to use another interface to assign the symbol and renderer to the layer. This interface is called IGeoFeatureLayer. As you can see from the many members of the IGeoFeatureLayer interface, it is primarily concerned with how to display the feature layer inside ArcMap. If you examine the CoClasses and Inherited Interfaces you find our old friend IFeatureLayer. This means that these two interfaces are interlated, so you can easily set the GeoFeatureLayer to the FeatureLayer and have any changes to the GeoFeatureLayer reflected in the FeatureLayer.
Dim pGeoFeatLyr as IGeoFeatureLayer
Next set the new variable equal to the featurelayer, and the renderer property to the simplerenderer. In both cases we need to use the Set keyword.
Set pGeoFeatLyr = pFeatLayer
pGeoFeatLyr.Renderer = pSimpRend
Save the project, and try out the tool.
Here is the final code for this sub routine. The commented out bit shows how to change the outline property using a simpleline.
For Further Reading:
Example on how to add a shapefile as a layer.