ArcGIS Server 9.3 REST API First Looks, Part 1 May 14, 2008
Posted by ruprict in ArcDeveloper.Tags: ArcGIS Server, JSON
1 comment so far
Image via Wikipedia
You may (or may not) know that I am one of the authors of the ArcDeveloper REST API, which is an open source REST API for ArcGIS Server that can be used against ArcGIS Server (AGS) 9.2, right now, free to all comers, no waiting, etc. If you know that, you probably know that ESRI is releasing it’s own REST API for AGS at 9.3, due out later this year. So, I thought I’d take a few posts to show you how the REST API (in beta) is looking. For the impatient, it looks great. For others that may not have the beta, read on.
So, first off, we are just gonna check out the very basics of the REST API. When you install 9.3, there is no real indication of anything all that different. The AGS Service Properties dialogs look (basically) the same, except for some (hugely exciting, but beyond the scope of this article) changes to the Caching tab. The real excitement comes when you start messing around with URLs against your map server. BTW, I am using Fiddler2 with the JSON Viewer plug-in to send requests to the server and look at the results.
AGS 9.0 and later have always exposed a SOAP endpoint for each service it publishes as well as service catalog. There is a whole SOAP API, which is my preferred method to interact with AGS services, and the URL to the SOAP service catalog looks like:
http://server/arcgis/services
Following this pattern, the REST API brings along a similar service catalog endpoint:
http://server/arcgis/rest/services
The new-kid-on-the-block REST URL is even cooler, though, as it formats the results into a cool “ArcGIS Service Explorer” that looks like:
Which allows you to look at metadata about your services, view them in Google Earth (how cool is that?), ArcMap, ArcGIS Explorer, or the new ArcGIS Javascript API (a subject of posts to come). Also, you can drill-down into your service and get layer info, spatial reference, unit info, and tiling information. In fact, on the tiling information, you can actually LOOK at some of the tiles. Other information includes supported operations that you can execute within the Explorer, like Export Map or Identify. Here’s a picture of the service page:
Another item the Service page has is a “Supported Interfaces” section, which I would call supported formats. The ones listed out of the box are REST and SOAP. Clicking on the REST link will give you all (well, most of) the information just mentioned, but in JSON format . Here, lemme show you:
The only difference between the URL to get to the cool HTML Services Explorer page and the URL that spits out JSON is the addition of a ‘f’ (for ‘format’) querystring parameter, like so:
http://server/arcgis/rest/services/ServiceName/MapServer?f=json
MMMMM….now, THAT’s some good REST.
There is similar love for supported operations, allowing you to crank out a quick image or perform a Find and look at the results. Also, the (impressively comprehensive) API reference is linked on the Services Explorer pages, which, oh by-the-way, takes you here.
Finally, the last thing I want to cover in this post is the REST admin inteface. If you go to the following URL in your browser:
http://server/arcgis/rest/admin
you’ll be hit with a login screen for the ArcGIS REST API Admin application. It’s pretty sparse right now, not even meriting a screen shot. Currently, you can use the admin site to tell AGS when to recycle the cached service responses and to turn on the REST Services Catalog. Now, that second option was nowhere to be found in my admin app, but the docs assure me it’s there.
So far, the only disappointment I have had is that ESRI has chosen not to use GeoJSON as their format to return features in (or, for that matter, even as an option). I don’t profess to know why, but I’ll try to float that to one of my contacts on the mother ship soon.
Summing up, the AGS REST API looks great before you even show a map on a site. I am hoping to go that in future posts.
Got (Remember the) Milk? May 6, 2008
Posted by ruprict in Recommendations.Tags: Remember The Milk
add a comment
Image by digitalsean via Flickr
I am, how you say, not very organized. I have tried billions of approaches of keeping task lists and to-dos that range from simply using Outlook to writing everything down on paper everyday. I have used Backpack (which is very nice, and I still use it for other things) but the reminders were too general. In all cases, the problem is never the system, but always the user. If I kept my tasks in Outlook, then I couldn’t get to them when I was reading my GMail account or if I use Backpack reminders, I had to have a window to Backpack open while doing other things. As it turns out, if it was even remotely awkward or required me to put forth any more effort than simply recording the task, then i didn’t do it.
My latest approach is Remember The Milk. RTM is an online task list service that focuses ONLY on managing your tasks. Being very Web 2.0y, it has an API, which means third party apps already abound. For example, RTM has a Mac OSX dashboard widget, a Quicksliver plugin, Twitter integration (supremely cool), GMail integration (finally, tasks with my GMail done right), and a Firefox extension. The pro version (a very reasonable $25/year) offers up integration with Windows Mobile, Blackberry, and iPhone/iPod Touch. You can also expose your tasks as an ICS feed and pull them into any calendar that supports it. For me, what this means is that wherever I am working, I am looking at the same task list. If I enter a task in Outlook while at work, I can mark it completed from the iPod Touch during my kid’s swim practice. Also, RTM works with Google Gears, so offline access is already there (Gears doesn’t support Safari yet, but that is coming…)
The tasks can be tagged (duh), arranged in lists, given a URL, duration, priority, due date, and (most righteously) a location. Now, I can look at a Google Map with my tasks on it. For example, some of my locations are Home, Harris Teeter (a grocery store), and Costco (a bulk grocery), allowing me to make Smart Lists where I split the list by location. You can publish lists to the world, email lists to your Inbox (or any other list) so my wife can give me my HoneyDo list is a way that makes my Nerd Muscle twitch with delight.
All in all, I am very encouraged by RTM and its offerings. I highly recommend that anyone who is as task-tarded as I am check it out.
Other RTM Resources
- Review on Freelanceswitch
- RTM is One of the Five Best GTD Apps, per Lifehacker. (GTD is on my todo list)
Great MonoRail Development Blog April 24, 2008
Posted by ruprict in Castle.Tags: MonoRail
1 comment so far
Ben Lovell has started a great series on incremental development with MonoRail. This is exactly what a new (and not so new) Castle or MVC or TDD developer needs to get going in the right direction. He even is posting the code on Google. Great work, Ben!
JSONP Support Added to ArcDeveloper REST April 23, 2008
Posted by ruprict in ArcDeveloper.Tags: REST
add a comment
I have previously blogged about the ArcDeveloper REST Service that a few of us ESRI .NET developers have thrown together. Until today, the only format you could request was GeoJSON. Now, through the miracle of something-I-needed-on-a-project, I have added JSONP support to the project. For those of you who don’t know what JSONP is, it is JSON wrapped in a javascript call. So, if you have JSON that looks like:
“type”: “FeatureCollection”,
“features”: [
{
"type": "Feature",
"geometry": null,
"properties": {
"Facility": "SI",
"Shape_Area": "4.93182604749608E-05"}
},
{
"type": "Feature",
"geometry": null,
"properties": {
"Facility":"Test",
"Shape_Area": "2.27729778462692E-05",
}
} ]
}
With JSONP, it looks like:
“type”: “FeatureCollection”,
“features”: [
{
"type": "Feature",
"geometry": null,
"properties": {
"Facility": "SI",
"Shape_Area": "4.93182604749608E-05"}
},
{
"type": "Feature",
"geometry": null,
"properties": {
"Facility":"Test",
"Shape_Area": "2.27729778462692E-05",
}
} ]
}’);
In a nutshell, JSONP was created to get around the pesky same-origin policy that browsers use to keep AJAX calls from making requests to a domain outside of the hosted site. This can be a real bugaboo, especially when you have a distributed application where, for example, ArcGIS Server is on one box, your web server is on another, and Sharepoint is on another. With JSONP, the client call can be wrapped in a <script> tag, which does not have to adhere to the same-origin policy. The <script> tag comes back with your JSON wrapped in a javascript call you specify (more on this is a second) and voila! we are cooking with gas.
In order to successfully call a JSONP service, you have to specify the callback function as a querystring parameter. As such, you have to know which querystring parameter the service is expecting to hold the callback function name. So, if we define a local javascript function called ‘jsonpCallComplete(json)’, then the script block comes back as:
Which fires your javascript call, passing in the JSON for you to do with as you please.
As a result of added this to the ArcDeveloper REST API, I had to make some “breaking” changes to the project. Firstly, the format must now be specified using the ‘f’ querystring parameter (i.e., f=json or f=jsonp) which maps to a ‘format’ parameter on the service methods. The value of the ‘f’ querystring parameter must match the name of the formatter, as configured in the Windsor configuration. Secondly, in order to pass the callback function name to the JSONP formatter, I chose to basically pass ALL the querystring paramters to every Formatter. That way, if another formatter needs a specific querystring parameter, it will supplied by the service manager for each request. Check the code for more details, if you are interested.
Dojo (a pretty danged good javascript framework) has native support for JSONP services, allowing you to, in essence, treat a cross-domain AJAX call as a same-origin AJAX call. The Dojo site has decent docs and a overview of Dojo is way beyond the scope of this post, but here is an example AJAX call to the ArcDeveloepr REST API on one box from a client site on another:
(you will need to do a dojo.require(’dojo.io.script’) for this to work)
url:”http://gisdev02/AGSRest/rest.svc/facility/Facilities”,
content: {
query:”name=’Airport’”,
f:’jsonp’ // specify the format
},
callbackParamName:’callback’, //specify the qs param to send the javascript func name
load:dojo.hitch(this,function(resp){
var featurecollection=dojo.fromJson(resp);
dojo.forEach(featurecollection.features,dojo.hitch(this,function(feat){
var opt = createNode(”option”);
opt.value=feat.properties.extent;
opt.innerHTML=feat.properties.Facility;// SEE? It’s JSON!
this.bookmarks.appendChild(opt);
}));
this.element.appendChild(this.bookmarks);})
}
Dojo is kind enough, in this case. to call the function defined in the ‘load’ property of the dojo.io.script.get method above. As far as your code knows, it’s just an AJAX call, but it only works for services that support JSONP.
So, that is JSONP support. I plan on checking in the code after some of the other ArcDeveloper types chime in on my “breaking” changes (mentioned above), which should be tomorrow.
By the way, for anyone developing there own JSONP services, here are a couple of things I ran into:
- Make sure you strip all whitespace out of the JSON before streaming it back to the client. I used a Regex for this.
- The trailing semi-colon ( ; ) on the JSONP call is, apparently, very important.
I’ll edit the post if I hit other issues in the testing.
Here’s What I Like! April 8, 2008
Posted by ruprict in Recommendations.Tags: Books, Tools
add a comment
I read a lot of blogs, and many of them have a page dedicated to utilities they use and books they like. Those blogs are cool, and I want to be like them. So, here is my page of tools and books (and frameworks). I plan to add to it as I remember stuff or find new toys. Feel free to suggest any.
Castle Windsor and WCF: A Match Made in Heaven April 1, 2008
Posted by ruprict in WCF.Tags: WCF, Windsor
add a comment
(Note: I’ve been out for a bit due to new recent additions to my life. The first was a (third) daughter born 2 weeks early and the second is an aggressive vasectomy schedule. Too much information, you say? I agree.)
If you’ve read any of my other posts, you know I dig Castle Windsor. It’s cool and allows a “special” developer like me to look pretty smart without actually being all that smart. I also really like WCF, as I feel it lead the charge of “things Microsoft started getting right”, again helping me look smart by abstracting all the WS-* and other service gobblety-gook out of my life. Since I REALLY like both of these technologies, I was hesitant to put them together, fearing I couldn’t take the shear joy it would create. However, Ayende and Craig have created the WCF Facility, allowing me to register my services on the Windsor container, and thus use dependency injection to handle all the stuff my services need. This post is a (hopefully) quick “how to” on this really cool facility.
(Note: This is my first foray into using Binsor for Windsor Configuration. All the smart guys were using it while I was still XMLing in the dark ages, so I had to follow suit.)
Step Uno: Write a Service
Let us create a simple WCF service. It looks like:
1 [ServiceContract()]
2 public interface IService1
3 {
4 [OperationContract]
5 string MyOperation1(string myValue);
6 [OperationContract]
7 void ThrowError();
8 }
9
10 public class Service1 : IService1
11 {
12 private ILogger logger; //Yes, yes, should init to a NullInstance.
13 public ILogger Logger
14 {
15 get { return logger; }
16 set { logger = value; }
17 }
18 #region IService1 Members
19 public string MyOperation1(string myValue)
20 {
21 logger.Info(“MyOperation1 called with {0}”,myValue);
22 return “Hello: “ + myValue;
23 }
24 public void ThrowError()
25 {
26 throw new Exception(“AAAAAAH!”);
27 }
28 #endregion
29 }
So, you see the service contract, you see the implementation. Please notice the following details:
- I added an ILogger property. This is a dependency.
- There is a method that just ralphs an error. Usually I am not as obvious with my errors, but at least I will be able to debug this one.
Usually, at this point, you’d open up the web.config file and start writing mountains of <service> tags and <behaviors> and all that. We aren’t going to do that. We are going to bring in Windsor to take care of all our needs. Yay!
Bring in da Noise, Bring in da Windsor
I am going to presume that you, the reader, knows about Windsor and, at least at a high level, how to configure it. Basically, for a web app (we are gonna host our services in IIS) you need to:
- Write the configuration files for Windsor, consisting of properties, facilities, and components. Go read the castleproject.org site for some decent examples.
- Register the container at startup.
As I stated previously, I am using Binsor to write my config files. (Tangent: I need to learn Boo. Ayende, is that book done yet?) Here goes:
1 import System;
2 import System.Reflection
3 import System.ServiceModel
4 import System.ServiceModel.Description from System.ServiceModel
5 import Castle.Facilities.Logging
6 import Castle.Facilities.WcfIntegration
7 import Ruprict.Grok.Castle.WcfIntegration.Core
8
9
10 facility LoggingFacility:
11 loggingApi = LoggerImplementation.Log4net
12 configFile = ‘log4net.config’
13
14 facility WcfFacility
15
16 component ‘windsor.service’, IService1, Service1:
17 ServiceModel = WcfServiceModel().Hosted() \
18 .AddEndpoints(WcfEndpoint.BoundTo(BasicHttpBinding()))
19
20 component ‘error.handler’,IServiceBehavior, LogExceptionHandler
21
22 component ‘metadata.behavior’,IServiceBehavior, ServiceMetadataBehavior:
23 HttpGetEnabled = true
This is Binsor 2.0 syntax, which is (from what I understand) yummier than ever. I do like it. It’s compact and readable and I don’t end up getting errors b/c I forgot to close a tag. Looking at the import statements, you see I bring in whatever namespaces I need, including my service namespace and any of it’s dependencies. Here, I’ve yanked in the ServiceModel stuff from WCF so I can hook up my IServiceBehavior extension classes, as well as some other Castle Facility namespaces (namely, the Logging facility). Then, I start defining things. I grab the LoggingFacility from Castle, telling it I am using Log4Net and pointing it at the right config file. Then it’s on to the star of our show, the WcfFacility. It seems odd that it just takes two words to bring in a facility that does so much. Ahh, simplicity, you are truly beautiful. Anyhoo, the first component is my WCF service (line 16) where you can see that I add a singular endpoint, bound to the BasicHttpBinding, um, binding. (NOTE: If you are using the BasicHttpBinding, like I am here, you don’t even have to specify a service model, as the facility will deduce it from the base address.) Finally, I add 2 more components, both IServiceBehaviors. For those that don’t know, IServiceBehavior is a way to extend WCF by create custom service (there are also endpoint and operation behaviors, all of which are treated the same by the WcfFacility) behaviors. In our example, I have created a LogExceptionHandler that will log errors to a log file. I also used a WCF core service behavior, ServiceMetatadataBehavior so I can use an HTTP GET to look at the service WSDL.
With the configuration defined, we have to get our IIS web app to use it, right? That’s done in the Global.asax by putting some code in the HttpApplication.Application_Start event.
1 public class Global : HttpApplication, IContainerAccessor
2 {
3 private static IWindsorContainer container;
4 protected void Application_Start(object sender, EventArgs e)
5 {
6 container = new WindsorContainer().Install(BinsorScript.FromFile(“windsor.boo”));
7 }
8 protected void Application_End(object sender, EventArgs e)
9 {
10 container.Dispose();
11 }
12
13 #region IContainerAccessor Members
14
15 public IWindsorContainer Container
16 {
17 get { return container; }
18 }
19
20 #endregion
21 }
That single line in Application_Start tells Windsor that I am using Binsor and the name of my config file. Gorgeous!
Now, in order to host a WCF service in IIS, you need a .svc file as an endpoint, and (usually) web.config configuration sections out the yin-yang. We still need the former, pointing it to our WindsorServiceHostFactory and using the name from our Binsor config file.
1 <%@ServiceHost Language=”C#”
2 Service=”windsor.service” Factory=”Castle.Facilities.WcfIntegration.WindsorServiceHostFactory”
3 %>
Now, when we crank up the application, the Windsor Container will attach the IServiceBehaviors in the config to our services, automatically wiring up my LogExceptionHandler and ServiceMetadataBehavior. This is especially sexy when you have multiple services in a single application and they ALL get the behaviors. Furthermore, since my service has an ILogger property (remember that?) and I am also using the LogFacility, it will create my Log4Net logger and give my service an instance. POW! BLAM! KABLOOEY! This is how we do it.
I was going to put some examples of the service getting called using WcfTestClient.exe (free with .NET 3.5) so you could see my error behavior doing it’s thang as well as prove to you that we don’t need no stinking web.config sections, but I find them superfluous in a post that already is larger than I wanted it to be. If you have more questions, hit me with a comment.
So, in summary, the keypoints here are:
- Windsor rocks, and the WcfFacility is all that and a bag of chips.
- You can have web.config <service> section-less WCF services, complete with auto-wired behaviors and dependencies.
- Ayende and Craig are really, really smart.
More Examples
These two posts (here and here) in the Castle Dev Google Group go through some more examples from Craig. You’ll even get to see me asking very basic questions. Also, Craig has promised he will post some stuff focusing on the client side of things, which should be very interesting.
Frustrations of a non-Agile Developer March 13, 2008
Posted by ruprict in Agile.Tags: Agile
5 comments
As I look across the landscape of GIS development, I see the tide turning to Agile Methodologies. Specifically, I see SCRUM taking hold and changing the way clients and implementors look at doing their work. Blogs like Chris’s make me fell all warm and fuzzy, as I dream of the day where massive, BigDesignUpFront (BDUF) fixed price RFPs are replaced with agile, iterative, pay-per-deliverable/sprint type requests. I am in constant conflict with the people I work with about this very subject. There are 2.4 billion reasons why I have to write this requirements document or that approach document. I push back with YAGNI type responses, begging for us to encourage the client to create user stories and dump the IEEE “shall” statements.
This scenario has played itself out since the beginning of the year several times. In the end, while my fellow developers agree with my plight, the program management and client just find me combative and difficult. I imagine they tense up everytime they have to ask me to write a new approach document (oh, you have no idea how I loathe approach documents) and recoil in expectation of my Agile Evangelical Sermon. I have taken the approach of writing user stories for the client as an example, then listing what I think the remaining stories are so they can finish them. After all, having he developer write user stories is akin to having the fox watch the henhouse. So, I get “This is great. We can use these.” and just when I think they are getting it someone asks “Have we mapped these back to the requirements?” and my hope is crumpled up and tossed toward the already full wastebasket of former agile dreams where it bounces to a rest on the floor.
Well, now that the self-pity party is over, I would love anyone to give me examples of getting stubborn, set-in-their-waterfall-ways Project Managers to listen to the merits of Agile. And I mean REALLY listen. I have sent articles of how to move to agile, I have authored e-mail novels on it, and I have discussed it over beers. Nothing takes hold. I’ve already been through the patronizing head-nodding, reassuring “uh-huh” discussions that I bet are eerily similar to what my wife gets when she tries to talk to me while I watch TV. They seem to agree, but their actions betray their nodding. Just today I was asked to validate a “high-level” estimate for a MASSIVE (roughly $3 million) project as they submit their RFP. It went like this:
Them: “Glenn our hours are X for the first iteration and Y for the second.”
Me: “2 iterations? Each about Y months long? Also, trying to estimate a project of this size and scope is ludicrous. (I hear the sigh on the other end of the IM) We should ask them if they are willing to spend a little money to go through an Iteration Zero, y’know? Create the Project Backlog, estimate number of sprints, then we can get a really good idea of cost and risk……” (I go on for a bit)
Them: “The RFP asked for a fixed price bid against the requirements that are in the RFP. We can’t just ignore that.”
Me:
I don’t know what to say. I see the problem and agree we can’t just ignore the RFP rules and submit whatever the hell we want. But estimating everything up front is just bad, bad mojo. It never works, and whoever wins the work will either 1) Stick with the BDUF approach and fail, or 2) Get the work, and then totally change the approach to something that has a snowball’s chance in hell of succeeding. Lather. Rinse. Repeat.
Anyway, if you find yourself reading this and have some suggestions beyond “Stop being some a whiny child,” I would love to hear them. Truth be told, most of what I know about Agile is “book knowledge”, as I have had precious few opportunities to apply the principles in my career.
ArcDeveloper REST: Windsor Brings the Party That Rocks the Body March 11, 2008
Posted by ruprict in ArcDeveloper.Tags: ArcDeveloper, REST
add a comment
In part III of my series on the ArcDeveloper REST API, I want to focus on how we use Windsor to configure the service. The best way to to that is to take a step back and look at why Windsor exists. Windsor is an Inversion of Control (IoC) container that provides a robust Dependency Injection (DI) framework. Both of those phrases have been blogged about by just about every human in existence, so if the Fowler link doesn’t help, then hit Google.
Background
In a nutshell, when you have classes that depend on other classes (and, really, what project doesn’t have that? Answer: Maintenance nightmare projects) a good pattern to follow is to supply the dependencies as opposed to instantiating them from within the class. The consummate example is:
public class ClassA{
private ClassB _classB;
public ClassA()
{
_classB=new ClassB(); //This is pure evil
}
}
The above code violates so many design patterns and principles that I had to have my 7-year old actually type it (he is a TOTAL hacker) b/c I couldn’t bring myself to do it. A better way to do it is:
public class ClassA{
private ClassB _classB;
public ClassA(ClassB injectedClassB)
{
_classB=injectedClassB; //This is sunshine and puppy dogs
}
}
That code is an example of constructor injection, b/c the dependency must be provided to the constructor or the class cannot be instantiated. There is also setter injection, where public properties are exposed and the dependencies are suppled there. Both approaches have their pros and cons, and I am of the opinion that, if you are simply using DI in either form, you are WAY ahead of the game.
Before I leave the background, I would be remiss if I didn’t point you to the Bitter Coder’s Wiki, where the best tutorials on Windsor live and party.
Great, So How’d Does the ArcDeveloper Rest Stuff Use Windsor?
Well, the Windsor container can use an external configuration section, say, you the web.config file.
<configSections>
<section name=“castle“ type=“Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor“/>
</configSections>
Once that is defined, you have three major configuration areas that you can play with: facilities, properties, and components (NOTE: You’ll find a separate .config file for each of these sections in the web project of our REST stuff.) Facilities are, basically, an extension to the Windsor container. There are many existing facilities, but the easiest to grok is probably the LoggingFacility. You register facilities on the container like so (from our facilities.config file):
<facilities>
<facility
id=“logging“
type=“Castle.Facilities.Logging.LoggingFacility, Castle.Facilities.Logging“
loggingApi=“log4net“
customLoggerFactory=“Castle.Services.Logging.Log4netIntegration.Log4netLogger“
/>
</facilities>
Here I have registered the Log4Net implemenation of the logging facility, which I could change to a different logging implementation by merely changing the “loggingApi” value (NOTE: I omit the logging.config that is required to configure Log4Net as it’s an, erm, implementation detail.) Once defined, any components registered on the container that have an ILogger public property will automagically get an instance of my Log4Net logger. That is just cool, man.
The rest of our time together will be spent working with components (we aren’t currently using properties right now, but they rock and the tutorials cover them well.) The ArcDeveloper REST API has services that provide the query functionality (like an ArcGIS Server provider) and formatters that take the results from the providers and transform them into the requested format. So, this means that we can register any provider and any formatter we might want to use with Windsor, and then all we have to do is tell the RESTServiceManager (our web service implementation class) about them. Here’s a picture (not quite 1000 words worth, but maybe a quick fin’s worth)
So, let’s quickly register the ArcGIS Service provider, which is implemented in the AGSMapService class.
<component id=“ags.service“ lifestyle=“pooled“ initialPoolSize=“2“ maxPoolSize=“2“
service=“ArcDeveloper.REST.Core.Interfaces.IRESTService, ArcDeveloper.REST.Core“
type=“ArcDeveloper.REST.ArcGIS.AGSMapService, ArcDeveloper.REST.ArcGIS“>
<parameters>
<name>TestService</name>
<description>Base map</description>
<connectionString>http://65.101.234.201/arcgis/services/gains/gains/MapServer</connectionString>
</parameters>
</component>
There you go. The AGSMapService class implements the IRESTService interface, and we provide the pertinent parameters. The “name” parameter (value=”TestService”) will be what is provided in the URI in order to specify that we want to use this service. The “connectionString” parameter is the URL of our ArcGIS Server Map Server object. Look closely at the attributes of the component, and you will see that we are pooling 2 instances of the service, which allows us to connect on first request and keep the object around for other requests. Since connecting to the server is a very expensive operation, we only have to live with it once. Windsor does this all for you! What do YOU do for Windsor? HMMM? That’s what I thought.
Anyway, lets show the formatter:
<component id=“geojson.formatter“ lifestyle=“transient“
service=“ArcDeveloper.REST.Core.Interfaces.IFormatter, ArcDeveloper.REST.Core“
type=“ArcDeveloper.REST.Core.Services.GeoJSONFormatter, ArcDeveloper.REST.Core“>
<parameters>
<name>geoJSON</name>
</parameters>
</component>
So, our GeoJSONFormatter implements the IFormatter interface. We name this one “geoJSON” which, whenever we write a second formatter, will be how the URI will refer to it when requesting the GeoJSON format. Oh, and the lifestyle of this bad boy is “transient”, meaning it’s created on request and disposed after request. This is not an expensive item, so that’s how we roll.
Finally, let’s take a look at the RESTServiceManager:
<component id=“rest.service“ lifestyle=“singleton“
service=“ArcDeveloper.REST.Core.Interfaces.IRESTServiceManager, ArcDeveloper.REST.Core“
type=“ArcDeveloper.REST.Core.RESTServiceManager, ArcDeveloper.REST.Core“>
<parameters>
<services>
<list>
<item>${ags.service}</item>
<!– <item>${other.service}</item>–>
</list>
</services>
<formatters>
<list>
<item>
${geojson.formatter}
</item>
</list>
</formatters>
</parameters>
</component>
You can see that our RESTServiceManager implements our IRESTServiceManager interface and we register our AGSMapService and GeoJSONFormatter with the service manager. The “services” and “formatters” properties of the service manager are lists, so we can add more services and more formatters simply by registering them on the container and adding them as an <item> to the list. Let me say it another way, just to drive the point home: If you wanted to register another ArcGIS Service map service with the RESTServiceManager, you would register with Windsor (so, copy the “ags.service”, give it a new id and change the <connectionString>
and then add an <item> to the <services> parameter with the component id (oh, you may have to restart your web app, as changes to the external config files do not kick off an app unload. If you don’t like it, pull all the config sections into web.config). After that, you can issue REST queries against the map service. It’s just that easy.
One final caveat. The IRESTServiceManager is not only our service manager, but it is also our WCF service contract. When you host a WCF service in IIS, it is created by the WCF Service Host factory, which means there is no way to register it with the Windsor container. That is bad, because if we can’t register the service manager, then we can register the services, and pretty much we have a web service that does bugger all. In order to get around this, the (man and legend) Ayende wrote a component that will allow you to register your WCF services with Windsor. The documentation for it is here, and is so eloquently written that I am stunned that I, er, I mean, the author didn’t get at least a Pulitzer nomination. The long and short of it is;
- Change the .svc file to use a custom ServiceFactory (from our rest.svc file)
<%@ ServiceHost Service="rest.service" Factory="Castle.Facilities.WcfIntegration.WindsorServiceHostFactory, Castle.Facilities.WcfIntegration" %> (GRRR....CopyAsHTML doesn't help me in .svc files)
- Instantiate the container at application start up, using the Global.Application_Start method (from our Global.asax.cs file)
protected void Application_Start(object sender, EventArgs e)
{
container = new WindsorContainer(new XmlInterpreter());
WindsorServiceHostFactory.RegisterContainer(container.Kernel);
_log = container[typeof(ILogger)] as ILogger;
_log.Info(“Services app started successfully.”);
}
That’s it. Now the WCF service will use the “rest.service” component we registered in the config file above.
So, this post got a bit wordy on me. I am young in my blogging ways, so I have trouble focusing. Also, I am easily distracted by shiny objects. In future posts, I plan to show how we leverage the Json.NET stuff for the GeoJSON formatter and maybe write a blog about something besides REST.
ArcDeveloper REST: U R I and I R U March 8, 2008
Posted by ruprict in ArcDeveloper.Tags: REST
1 comment so far
As I promised in my (first and) previous post, I am gonna quickly go over the very simple architecture of the ArcDeveloper REST API. This post covers how we expose our REST endpoint(s).
At a high level, we wanted to expose a single REST endpoint that allowed the caller to specify the “service”, “layer”, and query parameters in order to get geographic features returned in a format, which is also specified by the caller. So, attempting to be as RESTy as possible, the URI looks like:
http://<yourServer>/rest.svc/service/layer/3?format=geojson
It’s clear and easy and other URI endpoints are easy to deduce. Yup, we are RESTing we the best of them. Our main endpoint is the rest.svc, which is WCF’s way of exposing a service endpoint. The first question, then, we had to answer was how we would handle the URI routing. If the map services are registered dynamically (using Castle Windsor, which is arguably the greatest piece of code ever written. Ever. Not that I am prone to hyperbole. Oh, I’ll cover the configuration in the next post) then we have to dissect the various pieces of the URI to:
- Get a hold of the service requested.
- Query the right layer
- Use the right query parameters.
- Grab the right formatter.
Luckily for us, .NET 3.5 had recently been released, incorporating some cool changes in WCF, the coolest one being the URITemplate (good background here). The URITemplate allows us to define URI templates (duh) and map them to service methods (or OperationContracts, in WCF-speak) complete with the mapping of parameters of the method. Let’s see how we are using this now:
[OperationContract]
[WebGet(UriTemplate="{serviceName}/{featureType}/{featureId}?g={geom}",ResponseFormat=WebMessageFormat.Json)]
Stream GetSingleFeatureJSON(string serviceName, string featureType, string featureId, bool geom);
You can see how the dealies in the “{}” get mapped to the method parameters. Just to be complete, {serviceName} maps to the serviceName parameter, etc. The “geom” parameter specifies whether or not you want all the coordinates returned, because sometimes that can be a LOT of data. We also specify the response format here and the HTTP verb (WebGet== HTTP Get. I’ll give you 3 guesses how you specify HTTP POST. Wrong. It’s WebInvoke.)
So, I know what you are saying. “WTF? You have hard-coded the format INTO THE BLOODY METHOD NAME! I am not reading any more of this tripe.” Well, that’s your prerogative (Tangent: I had NO idea that was how to spell that word. No wonder Bobby Brown is on drugs) but know that I am adhering to the Last Responsible Minute (LRM) doctrine. We only need (geo)JSON right now, meaning, no one has written any other formatters. Once we have another format, we’ll put in a “format” parameter, remove the ResponseFormat and life will be all good. Weak excuse, you say? Maybe. Let’s, uh, move on.
The URIs the project currently supports are:
http://yourserver/rest.svc/services
Will return information about all of the services that are available to be queried.
http://yourserver/rest.svc/serviceName/layerName/OBJECTID?g=true
Will return the feature corresponding to the OBJECTID value from the layer specified by “layerName” in the service configured as “serviceName”.
http://yourserver/rest.svc/serviceName/layerName?query=whereClause&bbox=xmin,ymin,xmax,ymax&g=true
Will query layer named “layerName” in service configured as “serviceName” using the value in “whereClause” (like height>100 or streetname=’Main’ — don’t forget to escape your querystrings….) inside the bounding box specified by xmin, ymin and xmax, ymax.
As always, g=true returns the full coordinates of each geometry. Also, the serviceName corresponds to the name given in the Windsor configuration, NOT the name of the ArcGIS Server service.
In the next post (hopefully) I’ll cover the service and its configuration, showing you how we leverage the total kick-assness of Windsor to make life easy.
ArcDeveloper ArcGIS Server REST API is Breathing! March 5, 2008
Posted by ruprict in ArcDeveloper.Tags: ArcDeveloper, ArcGIS Server, REST
add a comment
So, the angst around the ArcGIS Server Web ADF is well known and I shan’t rehash it here (OK, just a little rehashing: the ADF is a bloated sack of vomit) but, rather, I’ll point you to the beginnings of an open source ArcGIS Server REST API (svn) at ArcDeveloper.NET. The project is very young (and always looking for contributors) and, in it’s current state, has the following capabilities:
- You can query single features by id (must be OBJECTID, ugh)
- You can query features with a where clause
- You can query features with a bbox (AND a where clause, if you want)
In this post, I will walk through what it takes to set it up and point it at one of your ArcGIS Server services.
What You’ll Need
Visual Studio 2008, implying .NET 3.5. You will have to build the solution, as we haven’t made an official release yet.
Optionally, I would have Fiddler, Firebug, and JSONViewer.
Step 1: Get the source
Using TortoiseSVN (or the svn client of your choosing), perform a checkout of the trunk (http://svn2.assembla.com/svn/arcdeveloper/ArcDeveloper.REST/trunk/)
Step 2: Open the solution
In the “Product” directory, you’ll find the VS2008 solution file. Like, open it or something. It consists of 5 projects, 2 of which are test projects. The other projects are the core interfaces and services, with the last one being a web project to show how to publish the REST service with WCF and a demo web page.
Step 3: Build the solution
Uh, in VS2008, select “Build Solution…”
Step 4: Use Dave’s Stuff
So Dave Bouwman, who I think is just the cat’s pajamas, has the demo site using an ArcGIS Server map service that he has been generous enough to provide. If you look in the ArcDeveloper.REST.Web project, you’ll find a config directory. Opening up the components.config file will show where the endpoint of the map services is specified. Looks alot like:
<component id=“ags.service“ lifestyle=“pooled“ initialPoolSize=“2“ maxPoolSize=“2“
service=“ArcDeveloper.REST.Core.Interfaces.IRESTService, ArcDeveloper.REST.Core“
type=“ArcDeveloper.REST.ArcGIS.AGSMapService, ArcDeveloper.REST.ArcGIS“>
<parameters>
<name>TestService</name>
<description>Base map</description>
<connectionString>http://65.101.234.201/arcgis/services/gains/gains/MapServer</connectionString>
</parameters>
</component>
Do you see the cool <connectionString> parameter? That’s Daves AGS service (I told you he was cool.) Run the site (it uses the VS Dev server now) and click “Load Polygon”. You’ll see a small polygon drawn over Yemen (erm, at least I think that is Yemen). Change the ID and draw some more.
Here’s what is happening:
A HTTP GET is issued to the local web service with a URI that looks alot like:
http://localhost:xxxx/rest.svc/TestService/Flyways/10?g=true
Let’s break down the URI, shall we? Starting with rest.svc, that is the WCF endpoint for the REST service. “TestService” points to our configuration (from above) file and tells the service which map we want to use. “Flyways” is the name of a layer in Dave’s service. “10″ is the id of the feature we want, and “g=true” tells the service to return the geometry (there are cases where you don’t want that, b/c it can make the size of the response baloon pretty quickly.) So, that is pretty RESTy, yes?
The response from that HTTP GET looks like:
{ "type": "Feature","geometry": {"type": "Polygon",
"coordinates": [
[
[ 44.3231, 14.2555], [43.2827,13.6294],[ 43.285, 13.6503], (…lots more coords…) ]
},
“properties”: {
“Ssp”: “maculosus”,
“Species”: “Burhinus capensis”,
“Shape_Length”: “4.59630121045653″,
“TSN”: “0″,
“extent”: “42.9447173339071,13.6294196034485,44.3230947030963,15.0950604545328″,
“SpeciesCod”: “BURCA”,
“WISDOM_SpeciesID”: “557″,
“OBJECTID”: “10″,
“Code”: ” “,
“Shape.area”: “1.28930099595786″
}
}
Which is valid GeoJson. Neat, eh?
I know what you are saying. “Glenn, why would I use this when ArcGIS Server 9.3 will have a REST API?? HMMM?” Well, that is a great question. The only answer I have is that you can use this now, contribute to it, and make the world a better place. Also, you can learn about a lot of stuff, like REST, WCF, GeoJSON, AJAX, the Castle Project stuff, and much, much more. Plus, there are some real brains on this project (I am not one of them) so you can sop up their wisdom as well. Also, I heard a nasty rumor that the AGS REST API wasn’t using GeoJSON, but a proprietary spatial JSON format, which is a bit disconcerting, if not totally expected.
In future posts I’ll break down the ArcDeveloper REST API architecture, showing how you can write your own providers and formatters. We still have a TON of work to do on it, but we’re on our way.




