<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-4314202365991499343</id><updated>2012-01-31T00:33:59.833-08:00</updated><title type='text'>August Town</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://augusttown.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://augusttown.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Yingqi Tang</name><uri>http://www.blogger.com/profile/00422288991527433117</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/--JG8BCe4IgI/Tja7MbVf-uI/AAAAAAAAAYQ/nde6VIRt3aY/s220/august_and_me.jpeg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>34</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4314202365991499343.post-67061216794132055</id><published>2011-07-17T15:21:00.001-07:00</published><updated>2011-07-17T15:21:03.608-07:00</updated><title type='text'>Back from ESRI User Conference 2011</title><content type='html'>&lt;p&gt;Finally done with the work and presentations of user conference this year, and I’m very glad to hear some positive feedback of my demo theatre presentation (“Leverage OGC capabilities of ArcGIS Server using Open Source GIS technology”) and technical workshop (“Leverage OGC capabilities of ArcGIS Server”) from users and colleagues. Now it’s time for a little break, then keep working and also look forward to the FOSS4G conference in Denver. &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4314202365991499343-67061216794132055?l=augusttown.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://augusttown.blogspot.com/feeds/67061216794132055/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://augusttown.blogspot.com/2011/07/back-from-esri-user-conference-2011.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/67061216794132055'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/67061216794132055'/><link rel='alternate' type='text/html' href='http://augusttown.blogspot.com/2011/07/back-from-esri-user-conference-2011.html' title='Back from ESRI User Conference 2011'/><author><name>Yingqi Tang</name><uri>http://www.blogger.com/profile/00422288991527433117</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/--JG8BCe4IgI/Tja7MbVf-uI/AAAAAAAAAYQ/nde6VIRt3aY/s220/august_and_me.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4314202365991499343.post-3909430423373024236</id><published>2011-06-20T11:46:00.001-07:00</published><updated>2011-06-20T11:46:47.142-07:00</updated><title type='text'>Google Earth Builder is really coming</title><content type='html'>&lt;p&gt;Just as many of others, I heard the announcement of &lt;a href="http://earth.google.com/builder"&gt;Google Earth Builder (GEB)&lt;/a&gt; and also watched the informative &lt;a href="http://www.directionsmag.com/webinars/view/exploring-google-earth-builder-with-the-product-team/175790"&gt;GEB webinar&lt;/a&gt; on Directions Magazine very recently, so Google’s Enterprise Cloud GIS is really coming (Q3, 2011 as mentioned in the webinar).&lt;/p&gt;  &lt;p&gt;Here are some of the the highlights I perceived from the the online webinar:&lt;/p&gt;  &lt;p&gt;1. Google Earth Builder will have similar functionalities as other existing Cloud GIS solutions, e.g. ArcGIS.com, GeoCommons, SimpleGeo, GloudGIS etc. So basically user can upload and manage their own dataset, style the dataset and mash them up with Google’s basemap layers as well as dataset shared by others, and finally share your mash up maps with others. But it doesn’t mean Google Earth Builder is just another Cloud GIS service at all, a few thing I notice will potential make it outstand. &lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;Supports both vector and raster dataset in their native projections;&lt;/p&gt;    &lt;p&gt;Leverage the base map layers and data from Google Maps and Google Earth API;&lt;/p&gt;    &lt;p&gt;Google’s superior and reliable cloud infrastructure;&lt;/p&gt; &lt;/blockquote&gt;  &lt;blockquote&gt;   &lt;p&gt;Same user experience of authentication and search as other Google products and services.&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;2. Support for OGC output &lt;/p&gt;  &lt;p&gt;KML is obviously supported and it’s now an OGC standard, but that is just a special case. Now WMS and WMTS are also mentioned to be supported soon which will make it possible for much more non-Google to consume data and map from Google Earth Builder. One thing great to have though is to allow users to upload/import data from various types of OGC services e.g. Web Coverage Service and Web Feature Service.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-7M7TI_OrrhE/Tf-VlUVjLsI/AAAAAAAAAUg/tLzA8W_vtD4/s1600-h/geb1%25255B4%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="geb1" border="0" alt="geb1" src="http://lh3.ggpht.com/-Qr7v03CrDyg/Tf-VlhHlfjI/AAAAAAAAAUk/c4_cEABHZz0/geb1_thumb%25255B2%25255D.png?imgmax=800" width="964" height="772" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Things that are missing or unknown to me from the webinar though are:&lt;/p&gt;  &lt;p&gt;1. Unknown about Metadata&lt;/p&gt;  &lt;p&gt;OGC/ISO 19115/19139 mentioned in Q&amp;amp;A section but I don’t think it will ever be supported.&lt;/p&gt;  &lt;p&gt;2. You won’t be able to directly access the data from Google Maps and Google Earth&lt;/p&gt;  &lt;p&gt;Finally when Google comes into an area, there is always question regarding whether Google will killer everybody in the same area or not. I don’t know about that but I feel if you’re doing the same thing as Google does, then you’d better be more creative and do it a little better.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4314202365991499343-3909430423373024236?l=augusttown.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://augusttown.blogspot.com/feeds/3909430423373024236/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://augusttown.blogspot.com/2011/06/google-earth-builder-is-really-coming.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/3909430423373024236'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/3909430423373024236'/><link rel='alternate' type='text/html' href='http://augusttown.blogspot.com/2011/06/google-earth-builder-is-really-coming.html' title='Google Earth Builder is really coming'/><author><name>Yingqi Tang</name><uri>http://www.blogger.com/profile/00422288991527433117</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/--JG8BCe4IgI/Tja7MbVf-uI/AAAAAAAAAYQ/nde6VIRt3aY/s220/august_and_me.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/-Qr7v03CrDyg/Tf-VlhHlfjI/AAAAAAAAAUk/c4_cEABHZz0/s72-c/geb1_thumb%25255B2%25255D.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4314202365991499343.post-22185686969585270</id><published>2011-06-13T22:35:00.001-07:00</published><updated>2011-06-13T22:53:57.611-07:00</updated><title type='text'>Cloud GIS, it’s all about sharing the map</title><content type='html'>Cloud GIS is obviously NOT just about sharing the map, but it is just cool to share a beautiful and useful map with others through blog, Facebook or Twitter.&lt;br /&gt;&lt;br /&gt;Google Maps&lt;br /&gt;&lt;iframe width="640" height="480" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="http://maps.google.com/maps/ms?ie=UTF8&amp;amp;hl=en&amp;amp;msa=0&amp;amp;msid=218335717276219304312.0004676354bc0004a17c0&amp;amp;ll=38.83222,-77.225647&amp;amp;spn=0.256746,0.439453&amp;amp;z=11&amp;amp;output=embed"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;small&gt;View &lt;a href="http://maps.google.com/maps/ms?ie=UTF8&amp;amp;hl=en&amp;amp;msa=0&amp;amp;msid=218335717276219304312.0004676354bc0004a17c0&amp;amp;ll=38.83222,-77.225647&amp;amp;spn=0.256746,0.439453&amp;amp;z=11&amp;amp;source=embed" style="color:#0000FF;text-align:left"&gt;Trip to Washington D.C.&lt;/a&gt; in a larger map&lt;/small&gt;&lt;br /&gt;&lt;br /&gt;ArcGIS.com&lt;br /&gt;&lt;iframe frameborder="0" height="512" marginheight="0" marginwidth="0" scrolling="no" src="http://www.arcgis.com/home/webmap/embedViewer.html?webmap=b69d3dbfe28b4bf89a92a2e431b9ab7a" width="764"&gt;&lt;/iframe&gt;  &lt;br /&gt;&lt;small&gt;&lt;a href="http://www.arcgis.com/home/webmap/viewer.html?webmap=b69d3dbfe28b4bf89a92a2e431b9ab7a" style="color: blue; text-align: left;" target="_blank"&gt;View Larger Map&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;   &lt;br /&gt;Geocommons &lt;br /&gt;&lt;iframe height="300" src="http://geocommons.com/maps/13216/embed" width="100%"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;GISCloud&lt;br /&gt;&lt;iframe src="http://www.giscloud.com/embed/?map=11195&amp;bound=1738077.55958559,5723923.98909473,1824604.27560441,5768028.15441527" width="600" height="400" frameborder="0"&gt;&lt;/iframe&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4314202365991499343-22185686969585270?l=augusttown.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://augusttown.blogspot.com/feeds/22185686969585270/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://augusttown.blogspot.com/2011/06/cloud-gis-its-all-about-sharing-map.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/22185686969585270'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/22185686969585270'/><link rel='alternate' type='text/html' href='http://augusttown.blogspot.com/2011/06/cloud-gis-its-all-about-sharing-map.html' title='Cloud GIS, it’s all about sharing the map'/><author><name>Yingqi Tang</name><uri>http://www.blogger.com/profile/00422288991527433117</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/--JG8BCe4IgI/Tja7MbVf-uI/AAAAAAAAAYQ/nde6VIRt3aY/s220/august_and_me.jpeg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4314202365991499343.post-4031772583513087273</id><published>2011-06-01T23:54:00.001-07:00</published><updated>2011-06-01T23:54:32.427-07:00</updated><title type='text'>Mashup WMS in GeoCommons</title><content type='html'>&lt;p&gt;WMS, whether dying or not, still has its place in the world of GIS. Given that many organizations have published their spatial data through WMS, it is a very important data source especially in the era of cloud GIS today. &lt;a href="http://geocommons.com/"&gt;GeoCommon&lt;/a&gt;’s is one of the first cloud GIS platform that allows users to add WMS as one type of data sources which can mashup on the base map. Follow the blog post “&lt;a href="http://developer.geoiq.com/blog/2011/02/01/wms-and-tiles/"&gt;Adding WMS and Tiles in GeoCommons and GeoIQ&lt;/a&gt;” I gave it a try using one public WMS called “&lt;a href="http://sampleserver1.arcgisonline.com/ArcGIS/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/WMSServer?VERSION=1.3.0&amp;amp;REQUEST=GetCapabilities&amp;amp;SERVICE=WMS"&gt;StatesCitiesRivers_USA&lt;/a&gt;”&amp;#160; from ArcGIS Online sample server and another from OSGeo.&lt;/p&gt;  &lt;p&gt;   &lt;br /&gt;After log in, select “Make a Map” and pick whatever base map as you like, which in my case is OSM.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/-8_WOqUClEfY/TecyzKt-37I/AAAAAAAAAT0/aBFviIj9LNY/s1600-h/image%25255B3%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/-NS2Vpi7MrTE/Tecy7uFKB3I/AAAAAAAAAT4/W6OHiEs05sE/image_thumb%25255B1%25255D.png?imgmax=800" width="870" height="557" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Click “+Add” at top-right corner, and select “Upload” tab and “Add a URL Link” tab, and finally select “Web Map Service” in the format dropdown list, and type in the url to WMS’s capabilities files (the url must point to the capabilities XML file either static or dynamic).&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/--KMfIM2dK8U/Tecy96vV7-I/AAAAAAAAAT8/eVai_MbPIc4/s1600-h/image%25255B7%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/-qsuA8wKSi9U/Tecy_2Z9iuI/AAAAAAAAAUA/U8gcUN-Gx1M/image_thumb%25255B3%25255D.png?imgmax=800" width="801" height="513" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Since ArcGIS Server supports WMS 1.3.0, so I tried this highest version of &lt;a href="http://sampleserver1.arcgisonline.com/ArcGIS/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/WMSServer?VERSION=1.3.0&amp;amp;REQUEST=GetCapabilities&amp;amp;SERVICE=WMS"&gt;capabilities file&lt;/a&gt;, but unfortunate it doesn’t display the WMS layers on top of base layer although it is able to get the list of layers from that WMS.&lt;/p&gt;  &lt;p&gt;Next I switched to version 1.1.1, which actually worked for me.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/-jI6PpeDB-uU/TeczD5wMuKI/AAAAAAAAAUE/hpPGDmky-6E/s1600-h/image%25255B11%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/-UXQRFV2EmK0/TeczM3BVynI/AAAAAAAAAUI/j_Pz1EnoB4E/image_thumb%25255B5%25255D.png?imgmax=800" width="871" height="496" /&gt;&lt;/a&gt;&lt;/p&gt;      &lt;p&gt;For the WMS overlay in GeoCommons, users are able to turn and off individual or all layers, and also change the opacity of the whole WMS overlay which obviously happens at client side (because WMS itself doesn’t support opacity). But none of other style or filter options are available to WMS layer. What is also observed is that WMS map is requested from server as multiple tiles instead of a single map image, which may be because GeoCommons platform currently doesn’t support any dynamic mapping service like WMS. Unless those tiles from WMS are cached at client-side, it’s not quite efficient.&lt;/p&gt;  &lt;p&gt;One big problem I see, which is also quite obvious from the screenshot , is that GeoCommons requests WMS map in EPSG:4326 even though the base map is Web Mercator (EPSG:3857 or EPSG&amp;quot;:900913 for most open source GIS community). This is caused by the fact that the public ArcGIS Server WMS service doesn’t list EPSG:900913 as one of the supported SRS. &lt;/p&gt;  &lt;p&gt;I then added the &lt;a href="http://vmap0.tiles.osgeo.org/wms/vmap0?request=getcapabilities&amp;amp;service=WMS&amp;amp;version=1.1.1"&gt;public WMS&lt;/a&gt; from OSGeo, which supports EPSG:900913, which is side proof of what happened to ArcGIS Online Sample Server.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/-reDMZ23dLxw/TeczPvsdz5I/AAAAAAAAAUM/QxYnZZAWqRE/s1600-h/image%25255B15%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/-6v1zf88ckcE/TeczTFvDzfI/AAAAAAAAAUQ/3375z7o5vFU/image_thumb%25255B7%25255D.png?imgmax=800" width="870" height="495" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Since ArcGIS Server WMS doesn’t support EPSG:900913 (not in the way when it’s named as 900913) at this point, the only workaround is to choose one of the base map marked as (4326).&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-mmRx5bnmpSE/Tecze3I6cwI/AAAAAAAAAUU/VnTPQlMCQ18/s1600-h/image%25255B19%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/-cb7q79JbhtE/Teczo2O4wTI/AAAAAAAAAUY/mpJkTfWPS8w/image_thumb%25255B9%25255D.png?imgmax=800" width="869" height="491" /&gt;&lt;/a&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;Now it overlays perfectly.&lt;/p&gt;  &lt;p&gt;Conclusion&lt;/p&gt;  &lt;p&gt;There are still a lot of data source published as WMS, and in my opinion there will only be more in the near future. So supporting WMS will give huge benefit to today’s Cloud GIS providers. It will be really nice to see similar WMS support in other major Cloud GIS player like &lt;a href="http://www.arcgis.com"&gt;ArcGIS.com&lt;/a&gt;, Google Maps and so on. &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4314202365991499343-4031772583513087273?l=augusttown.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://augusttown.blogspot.com/feeds/4031772583513087273/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://augusttown.blogspot.com/2011/06/mashup-wms-in-geocommons.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/4031772583513087273'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/4031772583513087273'/><link rel='alternate' type='text/html' href='http://augusttown.blogspot.com/2011/06/mashup-wms-in-geocommons.html' title='Mashup WMS in GeoCommons'/><author><name>Yingqi Tang</name><uri>http://www.blogger.com/profile/00422288991527433117</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/--JG8BCe4IgI/Tja7MbVf-uI/AAAAAAAAAYQ/nde6VIRt3aY/s220/august_and_me.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/-NS2Vpi7MrTE/Tecy7uFKB3I/AAAAAAAAAT4/W6OHiEs05sE/s72-c/image_thumb%25255B1%25255D.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4314202365991499343.post-5132988157788300771</id><published>2011-05-31T17:11:00.001-07:00</published><updated>2011-05-31T17:11:20.430-07:00</updated><title type='text'>World-wide routing, MapQuest + OpenStreetMap</title><content type='html'>&lt;p&gt;MapQuest was THE first and only web routing service I relied on. But once up on a time, I almost forgot its existence because of Google Maps, Yahoo, and even others. Recently MapQuest seems to be coming back really strong especially with its &lt;a href="http://developer.mapquest.com/web/products/open"&gt;Open Initiative&lt;/a&gt; and integration with OpenStreetMap data. One of the most amazing things I have seen is this &lt;a href="http://open.mapquest.com/link/5-dAZ9ADlp"&gt;Dublin-to-Shanghai&lt;/a&gt; world-wide routing:&lt;/p&gt;  &lt;p&gt;   &lt;br /&gt;&lt;img src="https://lh3.googleusercontent.com/v41mNAMMbJwry2lSSPRNpDE3B_ZxaagynWk1yju4e2MtSobqOt6zIJF1Tae_toNgsAzQrgXljhZh_Qa0NJBt0w2Ia1pRz3bz8UH2iHMYG4gDGza60w" width="604" height="315" /&gt;    &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;Although I don’t think it has much practical use to most people except for professional backpacker traveler, it is still amazing and seriously no one else can do it.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4314202365991499343-5132988157788300771?l=augusttown.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://augusttown.blogspot.com/feeds/5132988157788300771/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://augusttown.blogspot.com/2011/05/world-wide-routing-mapquest.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/5132988157788300771'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/5132988157788300771'/><link rel='alternate' type='text/html' href='http://augusttown.blogspot.com/2011/05/world-wide-routing-mapquest.html' title='World-wide routing, MapQuest + OpenStreetMap'/><author><name>Yingqi Tang</name><uri>http://www.blogger.com/profile/00422288991527433117</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/--JG8BCe4IgI/Tja7MbVf-uI/AAAAAAAAAYQ/nde6VIRt3aY/s220/august_and_me.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4314202365991499343.post-366212794668675237</id><published>2011-05-27T11:00:00.001-07:00</published><updated>2011-05-31T16:00:10.142-07:00</updated><title type='text'>Choosing China’s location check-in platform</title><content type='html'>&lt;p&gt;Like many other Chinese people (I am not talking about the those who were born or grown up here in States) here in Unite States, I’m facing a dilemma when choosing a location check-in game service to share location, pictures and tips with my friends. There seems to be quite a lot options. We have the popular &lt;a href="https://foursquare.com/"&gt;Foursquares&lt;/a&gt;, &lt;a href="http://gowalla.com/"&gt;Gowalla&lt;/a&gt;, and &lt;a href="http://www.facebook.com/"&gt;Facebook&lt;/a&gt;, and &lt;a href="http://www.google.com/latitude"&gt;Google Latitude&lt;/a&gt; which jumped in recently, plus &lt;a href="http://www.yelp.com"&gt;Yelp&lt;/a&gt;, &lt;a href="http://www.booyah.com/"&gt;Booyah&lt;/a&gt;, &lt;a href="http://www.facebook.com/MyTownTheGame"&gt;MyTown&lt;/a&gt;, and the list can go on and on. However, the problem is that many of my friends are in mainland China who want to see where I have been to and how I felt about those places, but they probably don’t want to install any of those clients on their mobile devices (actually even if they do, they can’t because many social networks applications are blocked in China). For the same reason I can’t see their move and tips either because obviously they don’t share it through any of the location check-in platform above. &lt;/p&gt;  &lt;p&gt;   &lt;br /&gt;The only workaround seems to be choosing one of China’s location check-in platform. Fortunately there is also a long list of those to choose, but none of them made a perfect solution. My top candidate list includes: &lt;a href="http://jiepang.com/"&gt;Jiepang (街旁)&lt;/a&gt;, &lt;a href="http://vld.sina.cn/"&gt;Sina (微领地)&lt;/a&gt;, &lt;a href="http://www.sifang.com"&gt;Sifang (四方)&lt;/a&gt;, and &lt;a href="http://k.ai/"&gt;kaikai (开开)&lt;/a&gt;. Which one to choose is certainly subjective to each individual person, but here is a list of things (ordered by priority) that I will base my choice on.&lt;/p&gt;  &lt;p&gt;   &lt;br /&gt;1. Size and accuracy of the existing foreign (U.S in this case) venue database;     &lt;br /&gt;2. Share check-in with friends on other check-in platform or social networks (Facebooks, Foursquares, Kaixin001, Sina Weibo etc);     &lt;br /&gt;3. Being able to add new venue.     &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;To me both #1 and #2 are must for a check-in platform be to usable, and #3 is nice to have but also important.    &lt;br /&gt;Venue database&lt;/p&gt;  &lt;p&gt;   &lt;br /&gt;This almost dominates my decision. Sina has its decisive advantage over others in its complete and accurate U.S venue database, which even overcomes its shortage in all other aspects. Just as an example, I live in Rancho Cucamonga, a small size city in southern California, which is not famous and probably unknown to most people in China. No one would expect a Chinese location check-in service to have much venues in this area in its databases. But I was really surprised the first time when I checked in at home, Sina accurately lists closest everything nearby which include a KFC, a RiteAid pharmacy, a Ralphs grocery store, a US Bank (a small local bank I’ve been to), …, and a lot of other small places. None of others have a similar or even close size and accuracy of the venue database as Sina does.&lt;/p&gt;  &lt;p&gt;   &lt;br /&gt;&lt;em&gt;Sharing check-in with others      &lt;br /&gt;&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;This is the second most important thing because first I obviously want my check-in to be shared with my friends in other check-in platforms or social networks, and second I obviously don’t want to repeat my check-in on every check-in platform either for my friends in U.S or in China. This seems to be possible in two aspects: either (a) I check-in once on one of them (e.g. Sina) and my check-in will spread out through synchronization to Facebook, Foursquare, Twitter, Kaixin (开心网), Weibo (新浪微博) an so on; or (b) I check in through a 3rd party proxy app which takes my check in and broadcast to all my location check in services. Option #b is only possible when everyone has a free open API for venues and check-in which is NOT true for any of the location check-in platform in China, so it is ruled out. Option a is possible in one direction (China check-in spread to U.S platform), and almost all Chinese location check-in services provide you such capability except for Sina. &lt;/p&gt;  &lt;p&gt;   &lt;br /&gt;&lt;em&gt;Adding new venues&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;   &lt;br /&gt;This is also very important to both users and location check-in service providers. I am true believer of the fact that a real complete and accurate venue database can only be evolved from volunteered geographic information from those locals who know where they live well. &lt;strike&gt;Unfortunately Sina again doesn’t support this feature while all other check-in platforms supports it which is quite disappointing to me.&lt;/strike&gt; As latest version of &lt;a href="http://vld.sina.cn/"&gt;Sina (微领地)&lt;/a&gt; goes, users are allowed to create new venue and submit to service provide for reviewing and I guess if it is approved then it will be added into existing venue database. This is a big leap to me and makes &lt;a href="http://vld.sina.cn/"&gt;Sina (微领地)&lt;/a&gt; outstands other options in my particular use case.&lt;/p&gt;  &lt;p&gt;   &lt;br /&gt;&lt;em&gt;Conclusion      &lt;br /&gt;&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;No one wins because neither Sina or any other candidates has both #1 and #2, but it provokes some interesting thinking on the current situation in the location check-in services market in China. Sina is close because it is the only one who is either interested in or can afford building a relatively complete and accurate foreign venue database. It is also easy to prove that those venues in sina’s database are not coming from any major open free sources (e.g. &lt;a href="http://www.OpenStreetMap.com"&gt;OpenStreetMap&lt;/a&gt; or Foursquare API etc.) So it’s probably from a commercial provider, which in turns logically explains why there are restrictions on adding new venues or modifying existing ones, and why check-in on vld.sina cannot be spread out to other platforms. Other location check-in platforms in China are still missing a solid venue database to start with, and importing from &lt;a href="http://www.OpenStreetMap.com"&gt;OpenStreetMap&lt;/a&gt; database or relying on the venue API from a popular U.S provider like Foursquares or so seem to be possible solutions.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4314202365991499343-366212794668675237?l=augusttown.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://augusttown.blogspot.com/feeds/366212794668675237/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://augusttown.blogspot.com/2011/05/choosing-chinas-location-check-in.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/366212794668675237'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/366212794668675237'/><link rel='alternate' type='text/html' href='http://augusttown.blogspot.com/2011/05/choosing-chinas-location-check-in.html' title='Choosing China’s location check-in platform'/><author><name>Yingqi Tang</name><uri>http://www.blogger.com/profile/00422288991527433117</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/--JG8BCe4IgI/Tja7MbVf-uI/AAAAAAAAAYQ/nde6VIRt3aY/s220/august_and_me.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4314202365991499343.post-1636164859487152795</id><published>2011-02-09T13:37:00.001-08:00</published><updated>2011-02-09T13:37:14.537-08:00</updated><title type='text'>Thoughts on OSM and the Bing automatic vector detect service</title><content type='html'>&lt;p&gt;In Mike Dobson’s recent blog post &lt;a href="http://blog.telemapics.com/?p=360"&gt;OSM vs. the Mechanical Turk – A New Option For Mappers?&lt;/a&gt;, he brought a point, which I think is more than reasonable:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;em&gt;“…I guess that when I thought about the term Volunteered Geographic Information, I made the mental leap that these volunteers were providing content in the form of spatial information reflecting the geographic areas in which they lived or with which they were more than casually familiar. It has now occurred to me that there may not always be a direct beneficial relationship between geographical knowledge and Volunteered Geographic Information…”&lt;/em&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;And then he did an analogous comparison between OSM and &lt;a href="http://en.wikipedia.org/wiki/The_Turk"&gt;Mechanical Turk&lt;/a&gt;, in which he points out that:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;em&gt;“…If the majority of OSM contributors to the UK database are spending their time digitizing imagery for the UK portion of the OSM database, as opposed to contributing GPS traces and attributes from paths along which they have traveled or know something about, how likely is it that the OSM effort in the UK benefits from local knowledge to the same extent that it benefits from “free” digitizing?…”&lt;/em&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;to him, the way how some people contribute to OSM database is similar to a modern version of Mechanical Turk. It looks like the data are contributed with valuable local geographical knowledge, but it is only artificially generated from aerial imagery.&lt;/p&gt;  &lt;p&gt;Now this reminds me of another &lt;a href="http://www.bing.com/community/site_blogs/b/maps/archive/2011/02/03/automatically-detect-roads-with-bing-aerial-imagery.aspx"&gt;exciting post&lt;/a&gt; I came across from Bing, in which Steve Coast announced the experimental service from Bing which can automatically derive street vector data from Bing aerial imagery. I am pretty sure it will boost the popularity of OSM in short term, but in the long run is it really helping?&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4314202365991499343-1636164859487152795?l=augusttown.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://augusttown.blogspot.com/feeds/1636164859487152795/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://augusttown.blogspot.com/2011/02/thoughts-on-osm-and-bing-automatic.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/1636164859487152795'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/1636164859487152795'/><link rel='alternate' type='text/html' href='http://augusttown.blogspot.com/2011/02/thoughts-on-osm-and-bing-automatic.html' title='Thoughts on OSM and the Bing automatic vector detect service'/><author><name>Yingqi Tang</name><uri>http://www.blogger.com/profile/00422288991527433117</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/--JG8BCe4IgI/Tja7MbVf-uI/AAAAAAAAAYQ/nde6VIRt3aY/s220/august_and_me.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4314202365991499343.post-3854736723191951993</id><published>2011-02-04T16:38:00.001-08:00</published><updated>2011-02-04T16:39:16.066-08:00</updated><title type='text'>Create your public WMS through www.GISCloud.com</title><content type='html'>&lt;p&gt;More and more cloud based GIS infrastructures are popping up these days. &lt;a href="http://www.giscloud.com/"&gt;GISCloud&lt;/a&gt; is just one of those many and probably not the most famous one. But it does have some interesting features that attracts me. Today, with its free account, I was able to quickly publish a public WMS (OGC Web Map Service) simply with a few clicks. Although the service seems a little buggy in it beta but the features are promising.&lt;/p&gt;  &lt;p&gt;To get started, simply go to &lt;a href="http://www.giscloud.com/"&gt;http://www.giscloud.com/&lt;/a&gt; and then sign up for a free account. After verification log in you will be able to things like upload/manage/share your dataset, create mashup maps with built-in layers (OSM, Google, etc.), your own dataset or public dataset shared by others, and of course your maps can be shared to other through different flavors. These are quite standard across different cloud GIS platforms nowadays.&lt;/p&gt;  &lt;p&gt;The aspect I like most of &lt;a href="http://www.GISCloud.com"&gt;www.GISCloud.com&lt;/a&gt; is the fact that it has a broad list of supported input/export formats, which by the way is very open source friendly. Especially the claim of “all OGC and GDAL formats”, which sounds quite powerful.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_qgvb6YVf5Fw/TUyb3Vv1M5I/AAAAAAAAASo/bZe13aqKOa4/s1600-h/image%5B3%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_qgvb6YVf5Fw/TUyb31f2coI/AAAAAAAAASs/b6UdCigSp08/image_thumb%5B1%5D.png?imgmax=800" width="593" height="317" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;© 2011 &lt;b&gt;&lt;a href="http://www.giscloud.com"&gt;GIS Cloud Ltd&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;The particular feature I tried is simply publishing a world boundary layer as a WMS. The user experience is quite smooth, since the world boundary is a default built-in layer, so I end up having a public WMS in literally just a few seconds.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_qgvb6YVf5Fw/TUyb4HUXZOI/AAAAAAAAASw/tq8-KeXTTJs/s1600-h/image%5B7%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_qgvb6YVf5Fw/TUyb4msG8VI/AAAAAAAAAS0/eWfMiZOH_4M/image_thumb%5B3%5D.png?imgmax=800" width="857" height="603" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Select “world” map on left, click Project-&amp;gt;Share and Publish&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_qgvb6YVf5Fw/TUyb46mZGpI/AAAAAAAAAS4/I1UWMkFKNbc/s1600-h/image%5B11%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_qgvb6YVf5Fw/TUyb5Z_8ciI/AAAAAAAAAS8/veMBTMM7zXw/image_thumb%5B5%5D.png?imgmax=800" width="865" height="609" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;In “Share and Publish” dialog, go to WMS tab and click “Enable”, and below that is the url endpoint of your public WMS.&lt;/p&gt;  &lt;p&gt;The next thing is simply consume it in a free 3rd party client like Gaia&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_qgvb6YVf5Fw/TUyb5mdJTFI/AAAAAAAAATA/1nC-wUzE7X0/s1600-h/image%5B15%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_qgvb6YVf5Fw/TUyb6kt_w4I/AAAAAAAAATE/4CToetGynoQ/image_thumb%5B7%5D.png?imgmax=800" width="870" height="582" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;This is really fantastic start, and I really like it.&lt;/p&gt;  &lt;p&gt;A few things to improve though:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;None of the built-in tiles layers (OSM, Google) come through as WMS, which I don’t think is technical issue but probably licensing issues;&lt;/li&gt;    &lt;li&gt;The world I configure in website seems to be in Mercator projection but the output WMS only claims EPSG:4326 in capabilities files;&lt;/li&gt;    &lt;li&gt;Still a little unstable when I enable/disable WMS back and forth with/without tiled layer like OSM, need to refresh the page to get rid of it.&lt;/li&gt; &lt;/ul&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4314202365991499343-3854736723191951993?l=augusttown.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://augusttown.blogspot.com/feeds/3854736723191951993/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://augusttown.blogspot.com/2011/02/create-your-public-wms-through.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/3854736723191951993'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/3854736723191951993'/><link rel='alternate' type='text/html' href='http://augusttown.blogspot.com/2011/02/create-your-public-wms-through.html' title='Create your public WMS through www.GISCloud.com'/><author><name>Yingqi Tang</name><uri>http://www.blogger.com/profile/00422288991527433117</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/--JG8BCe4IgI/Tja7MbVf-uI/AAAAAAAAAYQ/nde6VIRt3aY/s220/august_and_me.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_qgvb6YVf5Fw/TUyb31f2coI/AAAAAAAAASs/b6UdCigSp08/s72-c/image_thumb%5B1%5D.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4314202365991499343.post-3810946800286055163</id><published>2011-02-03T13:07:00.001-08:00</published><updated>2011-02-03T13:07:34.695-08:00</updated><title type='text'>Configure GDAL/OGR Python debug environment in Pydev on Windows</title><content type='html'>&lt;p&gt;Recently I am involved in a project that requires some Geospatial development in Python. So I decide to take this chance to get familiar with using GDAL/OGR in Python because it is the most popular open source GIS library plus it has a python binding. &lt;/p&gt;  &lt;p&gt;The first thing you need is obviously a Python development environment for GDAL/OGR. I know IDLE or even a plain text editor plus “print …” will do the job just fine, but I still prefer a decent IDE. Why not? Especially when it is free. So I choose the Eclipse based Python IDE Pydev. The only problem is that the process of configuring it with GDAL/OGR on Windows isn’t that obvious, or at least to me it is not. So in this post I will go through it step by step. &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;1. Install Python&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Installing Python is quite straight forward, just go to &lt;a href="http://www.python.org/"&gt;http://www.python.org/&lt;/a&gt;, pick up the installer for your version and OS, and install it. I would recommend Python 2.6 or 2.7 because there are pre-compiled GDAL/OGR python binding for both versions.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;2. Install GDAL/OGR Python binding &lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;First of all, this &lt;a href="http://vbkto.dyndns.org/sdk/"&gt;website&lt;/a&gt; is really helpful for you to get pre-compiled GDAL/OGR Python binding. It has different stable released version and the version latest trunk, and the best part is that it also has the installer version and non-installer (zipped version) versions for both 32bit and 64bit.&lt;/p&gt;  &lt;p&gt;&lt;u&gt;&lt;em&gt;Use installer version&lt;/em&gt;&lt;/u&gt;&lt;/p&gt;  &lt;p&gt;Find the .exe or .msi installer file, which usually comes with name GDAL-1.x.0-win32-py2.x.msi () e.g. here is the download link to the one for GDAL 1.8.0 32bit for python 2.7 (&lt;a href="http://vbkto.dyndns.org/sdk/Download.aspx?file=release-1500-gdal-mapserver\GDAL-1.8.0.win32-py2.7.msi"&gt;GDAL-1.8.0.win32-py2.7.msi&lt;/a&gt;). So basically just follow the installation wizards to finish the installation. Under the hood, my understanding is that the installer just simply copy a bunch of files and folder into \Lib\site-packages folder of your Python install location.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; the installer version requires you to have an appropriate version of Python installed and registered in registry first. The normal installation of Python takes care of the registry, but if not (e.g. you want to use the Python that comes with ArcGIS Server), then you will have to manually install a standard version of Python at a different location, install the GDAL Python binding, then copy those GDAL related files and folders back to the site-packages folder of that unregistered Python.&lt;/p&gt;  &lt;p&gt;&lt;u&gt;&lt;em&gt;Use non-installer version&lt;/em&gt;&lt;/u&gt;&lt;/p&gt;  &lt;p&gt;Instead of using installer version, you can choose to use a non-installer version which avoid adding stuff upon your vanilla Python install and registry. So from the same &lt;a href="http://vbkto.dyndns.org/sdk/"&gt;website&lt;/a&gt; just download those zip files with name e.g. &lt;a href="http://vbkto.dyndns.org/sdk/Download.aspx?file=release-1500-x64-gdal-mapserver.zip"&gt;release-1500-x64-gdal-mapserver.zip&lt;/a&gt; (don’t worry it comes with&amp;#160; a copy of UNM MapServer), and unzip it on your local disk. The “information” link gives you very detailed information about the folder structure and versions of packages included.&lt;/p&gt;  &lt;p&gt;After unzipping, I highly recommend you to take a look at the SDKShell.bat, which usually contains code similar to below:&lt;/p&gt;  &lt;p&gt;&lt;em&gt;:setenv2     &lt;br /&gt;SET PATH=%CD%\bin;%CD%\bin;%CD%\bin\gdal\python\osgeo;%CD%\bin\proj\apps;%CD%\bin\gdal\apps;%CD%\bin\ms\apps;%CD%\bin\gdal\csharp;%CD%\bin\ms\csharp;%CD%\bin\curl;%PATH%      &lt;br /&gt;SET GDAL_DATA=%CD%\bin\gdal-data      &lt;br /&gt;SET GDAL_DRIVER_PATH=%CD%\bin\gdal\plugins      &lt;br /&gt;SET PYTHONPATH=%CD%\bin\gdal\python\osgeo      &lt;br /&gt;SET PROJ_LIB=%CD%\bin\proj\SHARE&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;This basically tells you what environment variables and paths must be included in PATH system environment variable, which we need when later configuring Pydev.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;3. Install Pydev Python IDE&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Install Pydev IDE is also easy and straightforward. Either download it from &lt;a href="http://sourceforge.net/projects/pydev/"&gt;sourceforge&lt;/a&gt;, or update it through Eclipse by adding &lt;a title="http://pydev.org/updates" href="http://pydev.org/updates"&gt;http://pydev.org/updates&lt;/a&gt; in software update sites.&lt;/p&gt;  &lt;p&gt;After installation, just launch it.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;4. Configure GDAL/OGR Python debug environment in Pydev&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;This is the most trick part. Depend on whether you’re using the installer version or the non-installer version, the steps are different.&lt;/p&gt;  &lt;p&gt;First of all, you need to specify the Python interpreter in Pydev. So just go to Windows-&amp;gt;Preference. and navigate to “Pydev”&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_qgvb6YVf5Fw/TUsZDsWgo_I/AAAAAAAAASQ/Xmk2_ttEyEQ/s1600-h/image%5B7%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="https://lh6.googleusercontent.com/_qgvb6YVf5Fw/TUsZD4Mzx4I/AAAAAAAAASU/zQ0T-iLOEM4/image_thumb%5B3%5D.png?imgmax=800" width="800" height="605" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Click “New”, type in a name and specify the path to python.exe of your Python installation, and finally click ok.&lt;/p&gt;  &lt;p&gt;If you install GDAL Python binding with installer, then you should be good to go. &lt;/p&gt;  &lt;p&gt;If you install GDAL Python binding with non-installer zip file, then you will have to configure a few more things. Still in the “Python Interpreters” configure dialog box, at the bottom part, select “libraries” tab and click “New Folder” on the right, navigate and select the &amp;lt;GDAL_Python_Install&amp;gt;\bin\gdal\python, finally click ok.&lt;/p&gt;  &lt;p&gt;&lt;a href="https://lh5.googleusercontent.com/_qgvb6YVf5Fw/TUsZET8wREI/AAAAAAAAASY/cuE9xbKJm_Q/s1600-h/image%5B11%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_qgvb6YVf5Fw/TUsZEx1Qp_I/AAAAAAAAASc/mWmtUT3L4H0/image_thumb%5B5%5D.png?imgmax=800" width="803" height="606" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Assume you unzip the GDAL Python package at E:\programs\gdal\1.8.0, then you should add E:\programs\gdal\1.8.0\bin\gdal\python to the list of libraries. &lt;/p&gt;  &lt;p&gt;Next go to “Environment” table, and there you need to add a few environment variables to reflect the settings in SDKShell.bat (mentioned earlier) &lt;strong&gt;except for “PYTHONPATH”&lt;/strong&gt;.&lt;/p&gt;  &lt;p&gt;&lt;a href="https://lh5.googleusercontent.com/_qgvb6YVf5Fw/TUsZFKitz6I/AAAAAAAAASg/GGfi9_bgOss/s1600-h/image%5B15%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="https://lh3.googleusercontent.com/_qgvb6YVf5Fw/TUsZFbAmHAI/AAAAAAAAASk/9PTdGtel40I/image_thumb%5B7%5D.png?imgmax=800" width="803" height="606" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Assume again you unzip the GDAL Python package at E:\programs\gdal\1.8.0, then you should add following&lt;/p&gt; &lt;em&gt;Path=&amp;lt;your_original_path_variable&amp;gt;;E:\programs\gdal\1.8.0\bin;E:\programs\gdal\1.8.0\bin\gdal\python\osgeo;E:\programs\gdal\1.8.0\bin\proj\apps;E:\programs\gdal\1.8.0\bin\gdal\apps;E:\programs\gdal\1.8.0\bin\curl;   &lt;br /&gt;GDAL_DATA=E:\programs\gdal\1.8.0\bin\gdal-data    &lt;br /&gt;GDAL_DRIVER_PATH=E:\programs\gdal\1.8.0\bin\gdal\plugins    &lt;br /&gt;PROJ_LIB=E:\programs\gdal\1.8.0\bin\proj\SHARE&lt;/em&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; these environment setting only applies within your Pydev IDE when you choose to use this particular Python interpreter, and it doesn’t affect your existing system environment settings.&lt;/p&gt;  &lt;p&gt;Finally save the changes and you are good to go. To verify it, just launch a Python console window in Pydev, and execute following:&lt;/p&gt;  &lt;p&gt;&lt;em&gt;from osgeo import gdal&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;&lt;em&gt;from osgeo import ogr&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;&lt;em&gt;from osgeo import osr&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;They should just execute fine without any error message.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4314202365991499343-3810946800286055163?l=augusttown.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://augusttown.blogspot.com/feeds/3810946800286055163/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://augusttown.blogspot.com/2011/02/configure-gdalogr-python-debug.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/3810946800286055163'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/3810946800286055163'/><link rel='alternate' type='text/html' href='http://augusttown.blogspot.com/2011/02/configure-gdalogr-python-debug.html' title='Configure GDAL/OGR Python debug environment in Pydev on Windows'/><author><name>Yingqi Tang</name><uri>http://www.blogger.com/profile/00422288991527433117</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/--JG8BCe4IgI/Tja7MbVf-uI/AAAAAAAAAYQ/nde6VIRt3aY/s220/august_and_me.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='https://lh6.googleusercontent.com/_qgvb6YVf5Fw/TUsZD4Mzx4I/AAAAAAAAASU/zQ0T-iLOEM4/s72-c/image_thumb%5B3%5D.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4314202365991499343.post-6127365868762178074</id><published>2011-01-09T23:15:00.001-08:00</published><updated>2011-01-09T23:15:21.995-08:00</updated><title type='text'>Retire my T-Mobile G1</title><content type='html'>&lt;p&gt;I know this is not quite relevant to what I usually blogged, and I am also pretty sure that there are a lot better up-to-date articles on Internet which are related to the same topic. But I just feel I must write it down because it took me almost a day and half to finally retire my T-Mobile G1 phone, by that I mean two things:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Flash a custom Android 2.2 Froyo ROM on G1&lt;/li&gt;    &lt;li&gt;Activate G1 without T-Mobile data plan&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Here are detailed steps that worked for me, and the text are mostly copied and pasted from following two posts:&lt;/p&gt;  &lt;p&gt;&lt;a title="http://androidandme.com/2009/08/news/how-to-root-a-t-mobile-g1-and-mytouch-3g-android-phone/" href="http://androidandme.com/2009/08/news/how-to-root-a-t-mobile-g1-and-mytouch-3g-android-phone/"&gt;http://androidandme.com/2009/08/news/how-to-root-a-t-mobile-g1-and-mytouch-3g-android-phone/&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a title="http://istunner.com/?p=104" href="http://istunner.com/?p=104"&gt;http://istunner.com/?p=104&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Downgrade to &lt;/strong&gt;&lt;a href="http://koushikdutta.blurryfox.com/G1/DREAIMG-RC29.zip"&gt;&lt;strong&gt;RC29&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;Format your phone’s microSD card to FAT32 mode&lt;/li&gt;    &lt;li&gt;Download and unzip the &lt;a href="http://koushikdutta.blurryfox.com/G1/DREAIMG-RC29.zip"&gt;RC29&lt;/a&gt; or &lt;a href="http://koushikdutta.blurryfox.com/G1/DREAMIMG-RC7.zip"&gt;RC7&lt;/a&gt; image file. Copy the DREAMIMG.nbh file to the SD card. (RC29 for US, RC7 is for UK) &lt;/li&gt;    &lt;li&gt;Turn the device power off. &lt;/li&gt;    &lt;li&gt;Hold Camera button, and press Power button to entry bootloader mode. You should see a gray/white screen with instructions to flash your phone with the update on your SD card. If you don’t see that, make sure you followed the instructions properly. &lt;/li&gt;    &lt;li&gt;As per the on-screen instructions, press the Power button to start upgrade procedure. &lt;strong&gt;DO NOT DO ANYTHING TO INTERRUPT THIS PROCESS&lt;/strong&gt;. &lt;/li&gt;    &lt;li&gt;After it is finished, perform the restart your phone by pressing the Action key (press down on the scroll ball).&lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;&lt;strong&gt;Root the phone - &lt;strong&gt;Automatically replace your recovery image with FlashRec&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;The purpose of this method is to use a bluetooth security exploit that existed in Android build CRC1 or lower.&amp;#160; It downloads and installs the Cyanogen recovery image to your phone.&amp;#160; Using this method is preferred because it does not overwrite your radio and SPL file like the manual install method.&amp;#160; Future updates to Android are likely to block this hack, but you should always be able to revert to the official CRC1 build and gain access using this method.&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;1. Download and install the 1-click root app flashrec-20090815.apk&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;First we need to download the application file that was created by Zinx.&amp;#160; This file is named flashrec-20090815.apk and can be located at &lt;a href="http://zenthought.org/content/project/flashrec"&gt;Zen Thought&lt;/a&gt; or &lt;a href="http://www.ryebrye.com/blog/2009/08/16/android-rooting-in-1-click-in-progress/"&gt;RyeBlog&lt;/a&gt;.&amp;#160; I suggest downloading the file on your PC to avoid corruption.&amp;#160; Copy the app to your SD card and install it using your favorite file manager like &lt;a href="http://www.cyrket.com/package/com.metago.astro"&gt;Astro&lt;/a&gt; or &lt;a href="http://www.cyrket.com/package/de.android_telefonie.appmanager"&gt;AppManager&lt;/a&gt;.&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;2. Launch the FlashRec application and backup your current recovery image&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Before you can flash a new recovery image, you must backup your original once first.&amp;#160; Press the large button that says “Backup Recovery Image”&lt;/p&gt;  &lt;p&gt;&lt;a href="http://androidandme.com/wp-content/uploads/2009/08/zenflash1.png"&gt;&lt;img title="zenflash1" alt="Step 1. Press Backup Recovery Image" src="http://androidandme.com/wp-content/uploads/2009/08/zenflash1.png" width="320" height="480" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Step 1. Press Backup Recovery Image&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;3. Flash the new Cyanogen recovery image&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;After backing up your recovery image, you should see a message that says “Backed up”.&amp;#160; Now you should see the button “Flash Cyanogen Recovery 1.4″ light up.&amp;#160; Click the button to perform the flash and do not touch your phone until you see “Flashed new recovery image.”&amp;#160; When you see the flashed image below it is ok to reboot your phone and check that the new recovery image loaded.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://androidandme.com/wp-content/uploads/2009/08/zenflash4.png"&gt;&lt;img title="zenflash4" alt="Easiest root method to date." src="http://androidandme.com/wp-content/uploads/2009/08/zenflash4.png" width="320" height="480" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Easiest root method to date.&lt;/p&gt;  &lt;p&gt;Stop and Verify:&amp;#160; Reboot your phone with the steps below to see if the new recovery image is loading correctly.&lt;/p&gt;  &lt;p&gt;Power off your phone.    &lt;br /&gt;Press and hold the Home key and then power the phone back on.     &lt;br /&gt;You should be greeted by the new Cyanogen recovery image screen.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://androidandme.com/wp-content/uploads/2009/08/cmrecovery14.png"&gt;&lt;img title="cmrecovery14" alt="Cyanogen recovery image v1.4" src="http://androidandme.com/wp-content/uploads/2009/08/cmrecovery14.png" width="320" height="480" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Cyanogen recovery image v1.4&lt;/p&gt;  &lt;p&gt;Now that you have the Cyanogen recovery image, you can begin flashing custom builds of Android.&amp;#160; Jump to the bottom for Part 2: Install a custom build of Android.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Upgrade the Radio Image to “ota-radio-2_22_19_26I.zip”&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Since I downgraded G1 phone to RC29 using the DREAIMG.nbh method, the radio image was downgraded as well.&amp;#160; So I have to update to the latest Radio Image so you can load the latest roms and flash a new SPL file.&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;1. The new radio image has been posted on &lt;a href="http://www.htc.com/www/support/android/adp.html"&gt;HTC’s support website&lt;/a&gt;, &lt;a href="http://code.google.com/p/android-roms/downloads/list"&gt;Android Roms&lt;/a&gt; page, or &lt;a href="http://code.google.com/p/sapphire-port-dream/"&gt;Haykuro’s Sapphire Port&lt;/a&gt; page.&amp;#160; In order to apply the Radio update, we must download the file “ota-radio-2_22_19_26I.zip”.&lt;/p&gt;    &lt;p&gt;2.&amp;#160; After the file is downloaded, it must be placed in the root directory of your SD card.&amp;#160; This means to place it in the main directory and not inside any folder.&amp;#160; When the radio image file is placed on your SD card perform the following steps:&lt;/p&gt;    &lt;ul&gt;     &lt;li&gt;Power off your phone. &lt;/li&gt;      &lt;li&gt;Hold down the Home key and power the phone back on. &lt;/li&gt;      &lt;li&gt;Select apply any zip from sd or press Alt+A &lt;/li&gt;      &lt;li&gt;When prompted, hit Home to write the image file. &lt;/li&gt;      &lt;li&gt;When the radio image is finished updating, it should reboot your phone to the recovery screen.&amp;#160; You can select reboot system now or press Home+Back .&lt;/li&gt;   &lt;/ul&gt;    &lt;p&gt;&lt;strong&gt;Stop and Verify&lt;/strong&gt;:&amp;#160; You can check the version of your radio image to make sure it updated.&amp;#160; Go to Settings &amp;gt; About Phone &amp;gt; Baseband version.&amp;#160; The version number should end in 2.22.19.26l just like the original name of the update file.&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;&lt;strong&gt;Flash the SPL&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Flashing a new secondary program loader(SPL) will rewrite your internal partition tables and free up more space on your phone.&amp;#160; This new SPL is required to load certain roms.&amp;#160; You must have the latest radio installed before flashing the new SPL.&amp;#160; If you do not have the latest radio loaded, flashing the new SPL can prevent your phone from booting.&lt;/p&gt;  &lt;p&gt;Once again, this new SPL is only for:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Phone: US T-Mobile G1 &lt;/li&gt;    &lt;li&gt;Radio: 2.22.19.26I &lt;/li&gt;    &lt;li&gt;Recovery Image: Recovery Image that allows you to flash custom roms (Cyanogen)&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Flashing a new SPL will rewrite all your partition tables and will delete all data from your phone.&amp;#160; The main reason people flash a new SPL is because it increases your system partition from 70 MB to 90 MB.&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;Download the latest SPL(1.33.2005) from &lt;a href="http://code.google.com/p/sapphire-port-dream/"&gt;Haykuro’s Sapphire-port page&lt;/a&gt;. &lt;strong&gt;It must be 1.33.2005!!!&lt;/strong&gt;&lt;/li&gt;    &lt;li&gt;Copy the SPL file(spl-signed.zip) to your SD card. &lt;/li&gt;    &lt;li&gt;Reboot your phone into recovery mode.&amp;#160; (Power off phone.&amp;#160; Press and hold Home key then press power) &lt;/li&gt;    &lt;li&gt;Wait for Cyanogen’s recovery image to load and select “apply any zip from sd”.&amp;#160; Flash the spl-signed.zip file. &lt;/li&gt;    &lt;li&gt;After flashing the SPL, you must flash a custom rom or your phone will not boot.&lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;&lt;strong&gt;Flash Cyanogen Android 2.2 Custom ROM&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Now that the hard parts are over, we are ready to install a custom build of Android.&amp;#160; There are many to choose from and I will let you decide what to flash.&amp;#160; For daily use, I suggest using the latest &lt;a href="http://forum.xda-developers.com/showthread.php?t=537204"&gt;stable release from Cyanogen&lt;/a&gt;.&amp;#160; For a complete list of ROM’s, visit the &lt;a href="http://db.androidspin.com/android_build_information.asp"&gt;Android ROM Build Database&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;All rom files come in zip format.&amp;#160; Download the rom of your choice and place it in the root directory of you SD card.&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Power off your phone. &lt;/li&gt;    &lt;li&gt;Boot into recovery mode.&amp;#160; (Press and hold the Home key, then hit the power button). &lt;/li&gt;    &lt;li&gt;Before you flash a rom file, perform a wipe.&amp;#160; Press Alt+W to wipe the data and cache folders.&amp;#160; You must wipe when going form different builds of Android. &lt;/li&gt;    &lt;li&gt;Wait for the wipe to finish and the recovery&amp;#160; image to display again, then select “apply any zip from sd”.&amp;#160; Flash the zip file of your choice. &lt;/li&gt;    &lt;li&gt;After flashing any zip you should be able to reboot your system and watch it load to the home screen.&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;After changing Android builds, it came take several minutes for the first boot.&amp;#160; If you phone hangs on the animated Android logo, this is because you did not perform a wipe.&amp;#160; If you are stuck in a boot loop, return to the recovery image and make sure you wipe.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Activate wireless on the phone without data plan (only with a dead T-Mobile sim card)&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Note: &lt;/strong&gt;You must have latest Android SDK, adb tools and Android USB driver correctly installed first, so that your phone can be debugged through adb tools on your conputer. &lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;After the restart, you will see the “android” icon telling you to “touch” it. I did NOT touch it. I just touch an “blank” area so that the “emergency” call is not highlighted anymore. (you can test to see if you have root by typing on the PHONE this: &amp;lt;enter&amp;gt;reboot&amp;lt;enter&amp;gt;)     &lt;br /&gt;&lt;strong&gt;[&amp;lt;enter&amp;gt; means press the enter key]&lt;/strong&gt;&lt;/li&gt;    &lt;li&gt;Plug the phone to you computer. &lt;/li&gt;    &lt;li&gt;On the PHONE, type the following:      &lt;ul&gt;       &lt;li&gt;&lt;strong&gt;&amp;lt;enter&amp;gt;setprop persist.service.adb.enable 1&amp;lt;enter&amp;gt;&lt;/strong&gt;&lt;/li&gt;        &lt;li&gt;The computer will see a new device and try to install it. On the hardware setup, tell it to get the driver from where you unziped the &lt;strong&gt;Android USB Driver&lt;/strong&gt;. This will enable the phone to “really” connect with the computer and mount the SD. If you don’t do this, the following steps will NOT work. &lt;/li&gt;        &lt;li&gt;IF it didn’t work, restart by holding down the power button and turning off and on again. And do the &amp;lt;enter&amp;gt;reboot&amp;lt;enter&amp;gt; test. If the test did not work, you did something wrong. If it the test did work, it means you press Something! DO NOT PRESS ANY KEYS.&lt;/li&gt;     &lt;/ul&gt;   &lt;/li&gt;    &lt;li&gt;You should be connected with the computer now. On the COMPUTER:      &lt;ul&gt;       &lt;li&gt;Open command prompt (start&amp;gt;run&amp;gt;cmd) &lt;/li&gt;        &lt;li&gt;Type (all that are in &lt;strong&gt;bold&lt;/strong&gt;):           &lt;ul&gt;           &lt;li&gt;&lt;strong&gt;cd C:\android-sdk-windows-1.1_r1&lt;/strong&gt; (assuming that you unziped to this location) &lt;/li&gt;            &lt;li&gt;&lt;strong&gt;adb shell&lt;/strong&gt; (This should give you “#” on the next line. If you get an error message “No device” or something, it means that your COMPUTER is not connected to the PHONE) &lt;/li&gt;            &lt;li&gt;&lt;strong&gt;am start -a android.intent.action.MAIN -n com.android.settings/.Settings&lt;/strong&gt; (this will force the “Setting” menu to popup on your PHONE)&lt;/li&gt;         &lt;/ul&gt;       &lt;/li&gt;     &lt;/ul&gt;   &lt;/li&gt;    &lt;li&gt;Now on you PHONE, you should see the Settings menu. Activate the wifi setting and connect to your wifi. &lt;/li&gt;    &lt;li&gt;Once you have wifi connection, you can sign into your Google account.&lt;/li&gt; &lt;/ol&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4314202365991499343-6127365868762178074?l=augusttown.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://augusttown.blogspot.com/feeds/6127365868762178074/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://augusttown.blogspot.com/2011/01/retire-my-t-mobile-g1.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/6127365868762178074'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/6127365868762178074'/><link rel='alternate' type='text/html' href='http://augusttown.blogspot.com/2011/01/retire-my-t-mobile-g1.html' title='Retire my T-Mobile G1'/><author><name>Yingqi Tang</name><uri>http://www.blogger.com/profile/00422288991527433117</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/--JG8BCe4IgI/Tja7MbVf-uI/AAAAAAAAAYQ/nde6VIRt3aY/s220/august_and_me.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4314202365991499343.post-796398199739743184</id><published>2011-01-04T22:58:00.001-08:00</published><updated>2011-01-04T22:59:54.905-08:00</updated><title type='text'>Using MapQuest OSM Tiles in OpenLayers</title><content type='html'>With the recent launch of &lt;a href="http://blog.mapquest.com/2010/12/16/mapquest-proudly-introduces-open-mapquest-com/"&gt;Open.Mapquest U.S&lt;/a&gt;, people now have one more way to view and contribute to OpenStreetMap data. Among those cool things of new Open Mapquest initiative, my favorite is the look and feel of those tiles generated from OSM data:&lt;br /&gt;&lt;a href="http://lh4.ggpht.com/_qgvb6YVf5Fw/TSQWjehuwGI/AAAAAAAAAR0/Jzg3aiKn5GY/s1600-h/image%5B3%5D.png"&gt;&lt;img alt="image" border="0" height="689" src="http://lh6.ggpht.com/_qgvb6YVf5Fw/TSQWmweORHI/AAAAAAAAAR8/0dseKe8sUIM/image_thumb%5B1%5D.png?imgmax=800" style="border: 0px none; display: inline;" title="image" width="866" /&gt;&lt;/a&gt; &lt;br /&gt;In my own opinion, it has much better looking than the original OSM Mapnik or Osmarender style. Since the structure of Open.Mapquest tiles are exactly the same as OSM tiles except for the base url, it can not be easier to use them as base layer in an OpenLayers application. Here is how to do it:&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 768px;"&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2: &lt;span style="color: green;"&gt;// in place where you use to add OSM layer &lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3: &lt;span style="color: green;"&gt;// var osm = new OpenLayers.Layer.OSM();&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4: &lt;span style="color: green;"&gt;// map.addLayers([osm]);&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5: &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6: &lt;span style="color: green;"&gt;// create a new class for Open.Mapquest tiles&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7: OpenLayers.Layer.MapQuestOSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8:     &lt;span style="color: blue;"&gt;name&lt;/span&gt;: "&lt;span style="color: darkred;"&gt;MapQuestOSM&lt;/span&gt;",&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9:     &lt;span style="color: green;"&gt;//attribution: "Data CC-By-SA by &amp;lt;a href='http://openstreetmap.org/'&amp;gt;OpenStreetMap&amp;lt;/a&amp;gt;",&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;10:     sphericalMercator: &lt;span style="color: blue;"&gt;true&lt;/span&gt;,&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;11:     url: ' http:&lt;span style="color: green;"&gt;//otile1.mqcdn.com/tiles/1.0.0/osm/${z}/${x}/${y}.png',&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;12:     clone: &lt;span style="color: blue;"&gt;function&lt;/span&gt;(obj) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;13:         &lt;span style="color: blue;"&gt;if&lt;/span&gt; (obj == &lt;span style="color: blue;"&gt;null&lt;/span&gt;) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;14:             obj = &lt;span style="color: blue;"&gt;new&lt;/span&gt; OpenLayers.Layer.OSM(&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;15:             &lt;span style="color: blue;"&gt;this&lt;/span&gt;.&lt;span style="color: blue;"&gt;name&lt;/span&gt;, &lt;span style="color: blue;"&gt;this&lt;/span&gt;.url, &lt;span style="color: blue;"&gt;this&lt;/span&gt;.getOptions());&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;16:         }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;17:         obj = OpenLayers.Layer.XYZ.&lt;span style="color: blue;"&gt;prototype&lt;/span&gt;.clone.apply(&lt;span style="color: blue;"&gt;this&lt;/span&gt;, [obj]);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;18:         &lt;span style="color: blue;"&gt;return&lt;/span&gt; obj;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;19:     },&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;20:     CLASS_NAME: "&lt;span style="color: darkred;"&gt;OpenLayers.Layer.MapQuestOSM&lt;/span&gt;"&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;21: });&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;22:     &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;23: &lt;span style="color: blue;"&gt;var&lt;/span&gt; mapquestosm = &lt;span style="color: blue;"&gt;new&lt;/span&gt; OpenLayers.Layer.MapQuestOSM();&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;24: &lt;span style="color: green;"&gt;// map.addLayers([mapquestosm]);&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;25: ...&lt;/pre&gt;&lt;/pre&gt;This is a screen shot of how Open.Mapquest tiles look like in OpenLayers app&lt;br /&gt;&lt;a href="http://lh4.ggpht.com/_qgvb6YVf5Fw/TSQWnnshfjI/AAAAAAAAASA/NRwAy1V7TBc/s1600-h/image%5B8%5D.png"&gt;&lt;img alt="image" border="0" height="423" src="http://lh3.ggpht.com/_qgvb6YVf5Fw/TSQWohgkAPI/AAAAAAAAASE/tbz3KtivY2Q/image_thumb%5B4%5D.png?imgmax=800" style="border: 0px none; display: inline;" title="image" width="786" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4314202365991499343-796398199739743184?l=augusttown.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://augusttown.blogspot.com/feeds/796398199739743184/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://augusttown.blogspot.com/2011/01/using-mapquest-osm-tiles-in-openlayers.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/796398199739743184'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/796398199739743184'/><link rel='alternate' type='text/html' href='http://augusttown.blogspot.com/2011/01/using-mapquest-osm-tiles-in-openlayers.html' title='Using MapQuest OSM Tiles in OpenLayers'/><author><name>Yingqi Tang</name><uri>http://www.blogger.com/profile/00422288991527433117</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/--JG8BCe4IgI/Tja7MbVf-uI/AAAAAAAAAYQ/nde6VIRt3aY/s220/august_and_me.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_qgvb6YVf5Fw/TSQWmweORHI/AAAAAAAAAR8/0dseKe8sUIM/s72-c/image_thumb%5B1%5D.png?imgmax=800' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4314202365991499343.post-7200554264565249795</id><published>2010-12-20T00:49:00.001-08:00</published><updated>2010-12-20T00:50:56.152-08:00</updated><title type='text'>Cool feature called “Destination Maps” from Bing Maps</title><content type='html'>&lt;p&gt;Recently I found this cool feature called “&lt;a href="http://www.bing.com/maps/?style=r#JmNwPTQ3LjY2ODk5ODk5OTk5OTk4fi0xMjIuMTI0MDAxJmx2bD0xMSZkaXI9MCZzdHk9ciZhcHA9NTA3MzQ="&gt;Destination Maps&lt;/a&gt;” from Bing Maps, which basically allows you to pick up a location, adjust an extent and then create a map out of it. The real cool thing though is the “sketchy” and “treasure” map style. BTW you can export the final map as PDF or JPEG format in different size. Here are the two maps I created:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_qgvb6YVf5Fw/TQ8YpRs6pfI/AAAAAAAAARg/zmCID6_SpQk/s1600-h/yingqi_home_destination_maps%5B4%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="yingqi_home_destination_maps" border="0" alt="yingqi_home_destination_maps" src="http://lh3.ggpht.com/_qgvb6YVf5Fw/TQ8Yp1hQqTI/AAAAAAAAARk/oNJaKNZ6f18/yingqi_home_destination_maps_thumb%5B2%5D.jpg?imgmax=800" width="772" height="772" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_qgvb6YVf5Fw/TQ8YqMAmEgI/AAAAAAAAARo/kOuNcxDGQp0/s1600-h/yingqi_home_destination_maps_2%5B3%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="yingqi_home_destination_maps_2" border="0" alt="yingqi_home_destination_maps_2" src="http://lh5.ggpht.com/_qgvb6YVf5Fw/TQ8Yq6qO9oI/AAAAAAAAARs/MkF1vjf67Ts/yingqi_home_destination_maps_2_thumb%5B1%5D.jpg?imgmax=800" width="772" height="772" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Looks good, huh? It makes a good poster. &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4314202365991499343-7200554264565249795?l=augusttown.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://augusttown.blogspot.com/feeds/7200554264565249795/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://augusttown.blogspot.com/2010/12/cool-feature-called-destination-maps.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/7200554264565249795'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/7200554264565249795'/><link rel='alternate' type='text/html' href='http://augusttown.blogspot.com/2010/12/cool-feature-called-destination-maps.html' title='Cool feature called “Destination Maps” from Bing Maps'/><author><name>Yingqi Tang</name><uri>http://www.blogger.com/profile/00422288991527433117</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/--JG8BCe4IgI/Tja7MbVf-uI/AAAAAAAAAYQ/nde6VIRt3aY/s220/august_and_me.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_qgvb6YVf5Fw/TQ8Yp1hQqTI/AAAAAAAAARk/oNJaKNZ6f18/s72-c/yingqi_home_destination_maps_thumb%5B2%5D.jpg?imgmax=800' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4314202365991499343.post-6798418622159492654</id><published>2010-11-20T15:37:00.001-08:00</published><updated>2010-11-20T15:42:55.884-08:00</updated><title type='text'>Hack OpenGeo Styler to work with generic WMS servers</title><content type='html'>As part of the &lt;a href="http://opengeo.org/products/suite/"&gt;OpenGeo Suite&lt;/a&gt;, &lt;a href="http://projects.opengeo.org/styler"&gt;Styler&lt;/a&gt; is a web based WMS viewer plus SLD editor built upon &lt;a href="http://openlayers.org/"&gt;OpenLayers&lt;/a&gt; and &lt;a href="http://www.geoext.org/"&gt;GeoExt&lt;/a&gt;. Although it uses WMS and SLD protocols, current release of Styler must be coupled with a running GeoServer instance. So it is impossible for Styler to work with a generic WMS server due to the following issues:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;WMS layers must come from GeoServer instance on the same machine as “/geoserver/wms?” &lt;/li&gt;&lt;li&gt;Every WMS layer must have has a WFS feature type at back end which must come from “/geoserver/wfs?” &lt;/li&gt;&lt;li&gt;There is no where to configure proxy so it doesn’t work with a remote server &lt;/li&gt;&lt;li&gt;Instead of WMS GetStyle, Styler is using GeoServer REST API (not a standard) to load SLD from WMS &lt;/li&gt;&lt;li&gt;Instead of WMS PutStyle, Styler is using GeoServer REST API (not a standard) to push and persist styles back to WMS &lt;/li&gt;&lt;li&gt;Styler is not using either standard WMS parameter “SLD” or “SLD_BODY” to make stateless change of map styles&lt;/li&gt;&lt;li&gt;Styler requires the WMS to support DescribeLayer interface &lt;/li&gt;&lt;/ul&gt;By briefly looking through its source code, I found it’s not too hard to overcome those issues and make Styler talk to a generic WMS server (e.g. ArcGIS Server) as long as the target WMS meets following prerequisites:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;WMS must support EPSG:900913 (EPSG:3857 or Esri’s EPSG:102110 is ok but requires a little tweak in OpenLayers) &lt;/li&gt;&lt;li&gt;WMS must support GetStyle interface &lt;/li&gt;&lt;li&gt;Each layer in WMS must have a WFS feature type at back end in order to get attributes list &lt;/li&gt;&lt;li&gt;Each style of a WMS layer must have a SLD definition which can be retrieved through GetStyle &lt;/li&gt;&lt;li&gt;Optionally WMS needs to support PutStyle interface if one wants to persist SLD styles at server side &lt;/li&gt;&lt;li&gt;Optionally WMS needs to support SLD_BODY to make stateless change of rendering and symbology&amp;nbsp; &lt;/li&gt;&lt;/ul&gt;So in this article, I’m going to go through the basic steps to tweak the OpenGeo Styler to communicate with a non-GeoServer WMS:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1.&lt;/b&gt; Checkout Styler source code from &lt;a href="http://svn.opengeo.org/suite/trunk/styler" title="http://svn.opengeo.org/suite/trunk/styler"&gt;http://svn.opengeo.org/suite/trunk/styler&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2.&lt;/b&gt; Build Styler&lt;br /&gt;&lt;blockquote&gt;&lt;i&gt;sudo easy_install jstools&lt;/i&gt; &lt;/blockquote&gt;&lt;blockquote&gt;&lt;i&gt;mkdir script&lt;/i&gt;&lt;/blockquote&gt;&lt;blockquote&gt;&lt;i&gt;jsbuild build.cfg -o script&lt;/i&gt;&lt;/blockquote&gt;After a successful build there should be 4 js files in script folder.&lt;br /&gt;&lt;b&gt;Note:&lt;/b&gt; I only found out how I can build it on Linux, and I didn’t try on Windows&lt;br /&gt;&lt;br /&gt;&lt;b&gt;3.&lt;/b&gt; Setup debug environment for Styler&lt;br /&gt;To launch Styler application simply follow the instructions &lt;a href="http://projects.opengeo.org/styler/wiki/howto/build"&gt;here&lt;/a&gt;. By default, the index.html references merged and compressed js file in “script” folder, in order to modify and debug Styler you will need to reference those uncompressed js files in index.html as below:&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 768px;"&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2: &amp;lt;script src="&lt;span style="color: darkred;"&gt;../styler/externals/openlayers/lib/OpenLayers.js&lt;/span&gt;"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3: &amp;lt;script src="&lt;span style="color: darkred;"&gt;../styler/externals/ext/adapter/ext/ext-base.js&lt;/span&gt;"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4: &amp;lt;script src="&lt;span style="color: darkred;"&gt;../styler/externals/ext/ext-all.js&lt;/span&gt;"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5: &amp;lt;script src="&lt;span style="color: darkred;"&gt;../styler/externals/geoext/lib/GeoExt.js&lt;/span&gt;"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6: &amp;lt;script src="&lt;span style="color: darkred;"&gt;../styler/script/gxp.js&lt;/span&gt;"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: yellow; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7: &amp;lt;script src="&lt;span style="color: darkred;"&gt;../styler/lib/Styler.js&lt;/span&gt;"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: yellow; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8: &amp;lt;script src="&lt;span style="color: darkred;"&gt;../styler/lib/Styler/dispatch.js&lt;/span&gt;"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: yellow; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9: &amp;lt;script src="&lt;span style="color: darkred;"&gt;../styler/lib/Styler/ColorManager.js&lt;/span&gt;"&amp;gt;&amp;lt;/script&amp;gt;        &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: yellow; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;10: &amp;lt;script src="&lt;span style="color: darkred;"&gt;../styler/lib/Styler/SchemaManager.js&lt;/span&gt;"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: yellow; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;11: &amp;lt;script src="&lt;span style="color: darkred;"&gt;../styler/lib/Styler/SLDManager.js&lt;/span&gt;"&amp;gt;&amp;lt;/script&amp;gt;      &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;12: &amp;lt;script src="&lt;span style="color: darkred;"&gt;../styler/externals/ux/colorpicker/color-picker.ux.js&lt;/span&gt;"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;13: ...&lt;/pre&gt;&lt;/pre&gt;Now you should be able to debug into OpenLayers and Styler in browser.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;4.&lt;/b&gt; Hack the code&lt;br /&gt;You don’t actually need to touch any code in OpenLayers or GeoExt, all changes are within three .js files in Styler itself, which are:&lt;br /&gt;&lt;blockquote&gt;\lib\Styler.js &lt;br /&gt;\lib\Styler\SLDManager.js &lt;br /&gt;\lib\Styler\SchemaManager.js&lt;/blockquote&gt;Code changes in those files are explained in details below. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;Code Changes in Styler.js&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;a. Add three properties for class Styler&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 768px;"&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: &lt;span style="color: green;"&gt;// right before the “constructor”, and after other properties&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2: ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3: wmsUrl: "&lt;span style="color: darkred;"&gt;/geoserver/wms?&lt;/span&gt;",&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4: wfsUrl: "&lt;span style="color: darkred;"&gt;/geoserver/wfs?&lt;/span&gt;",  &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5: proxyUrl: "&lt;span style="color: darkred;"&gt;&lt;/span&gt;",&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6: ...&lt;/pre&gt;&lt;/pre&gt;&lt;br /&gt;b. Within Styler.js replace “/geoserver/wms?” or “/geoserver/ows?” with “this.proxyUrl + this.wmsUrl”, and replace “/geoserver/wfs?” with “this.proxyUrl + this.wfsUrl”. This is to allow user to point Styler to any WMS, either remote or local.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Note&lt;/b&gt;: there is one exception though, in method createLayers() you just need to replace “/geoserver/wms?” with this.wmsUrl because WMS layer doesn’t need a proxy to request map image.&lt;br /&gt;&lt;br /&gt;c. Avoid using WMS DescribeLayer&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 768px;"&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: &lt;span style="color: green;"&gt;//locate method:&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2: describeLayers: &lt;span style="color: blue;"&gt;function&lt;/span&gt;(callback) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:   ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4: }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5: &lt;span style="color: green;"&gt;//Comment out original code and add following chunk of code&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6: describeLayers: &lt;span style="color: blue;"&gt;function&lt;/span&gt;(callback) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: yellow; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7: &lt;span style="color: blue;"&gt;for&lt;/span&gt; (&lt;span style="color: blue;"&gt;var&lt;/span&gt; i=0, ii=&lt;span style="color: blue;"&gt;this&lt;/span&gt;.wmsLayerList.&lt;span style="color: blue;"&gt;length&lt;/span&gt;; i&amp;lt;ii; ++i) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: yellow; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8:                   config = &lt;span style="color: blue;"&gt;this&lt;/span&gt;.wmsLayerList[i];&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: yellow; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9:                   &lt;span style="color: green;"&gt;// assume each WMS layer has a backend WFS layer&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: yellow; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;10:                   &lt;span style="color: blue;"&gt;this&lt;/span&gt;.layerList.push(config);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: yellow; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;11:             }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: yellow; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;12:             callback();&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;13:   }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;14: &lt;span style="color: green;"&gt;//here assumption is that every WMS layer has a WFS feature type at back end&lt;/span&gt;&lt;/pre&gt;&lt;/pre&gt;&lt;br /&gt;d. If your WMS supports Google Mercator projection as something other than “EPSG:900913”, In method createLayers() you will need to add an extra parameter (e.g. “srs: ‘EPSG:3857’”) when creating each WMS layer&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 768px;"&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: createLayers: &lt;span style="color: blue;"&gt;function&lt;/span&gt;() {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2: ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:     layers.push(&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:     &lt;span style="color: blue;"&gt;new&lt;/span&gt; OpenLayers.Layer.WMS(&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:         config.title, &lt;span style="color: blue;"&gt;this&lt;/span&gt;.wmsUrl, {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6:         layers: config.&lt;span style="color: blue;"&gt;name&lt;/span&gt;,&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7:         styles: config.styles[0].&lt;span style="color: blue;"&gt;name&lt;/span&gt;,&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8:         transparent: &lt;span style="color: blue;"&gt;true&lt;/span&gt;,&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9:         format: "&lt;span style="color: darkred;"&gt;image/png&lt;/span&gt;",&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: yellow; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;10:         srs: "&lt;span style="color: darkred;"&gt;EPSG:3857&lt;/span&gt;"  &lt;span style="color: green;"&gt;// EPSG:3857, EPSG:102110 or EPSG:102113 etc.&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;11:         }, {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;12:     }));&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;13: ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;14: }&lt;/pre&gt;&lt;/pre&gt;&lt;br /&gt;e. Pass proxyUrl, wmsUrl and wfsUrl into SLDManager and SchemaManager&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 768px;"&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: getSchemas: &lt;span style="color: blue;"&gt;function&lt;/span&gt;(callback) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2:             &lt;span style="color: blue;"&gt;this&lt;/span&gt;.schemaManager = &lt;span style="color: blue;"&gt;new&lt;/span&gt; Styler.SchemaManager(&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:             &lt;span style="color: blue;"&gt;this&lt;/span&gt;.map,&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:             {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:               proxyUrl: &lt;span style="color: blue;"&gt;this&lt;/span&gt;.proxyUrl,&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6:               wfsUrl: &lt;span style="color: blue;"&gt;this&lt;/span&gt;.wfsUrl&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7:             }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8:           );&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9: &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;10:   getStyles: &lt;span style="color: blue;"&gt;function&lt;/span&gt;(callback) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;11:         &lt;span style="color: blue;"&gt;this&lt;/span&gt;.sldManager = &lt;span style="color: blue;"&gt;new&lt;/span&gt; Styler.SLDManager(&lt;span style="color: blue;"&gt;this&lt;/span&gt;.map, {'proxyUrl': &lt;span style="color: blue;"&gt;this&lt;/span&gt;.proxyUrl});&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;12:             &lt;span style="color: blue;"&gt;this&lt;/span&gt;.sldManager.loadAll(callback);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;13:       }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;14: &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;15:   initEditor: &lt;span style="color: blue;"&gt;function&lt;/span&gt;() { {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;16:   …&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;17:  &lt;span style="color: blue;"&gt;this&lt;/span&gt;.sldManager = &lt;span style="color: blue;"&gt;new&lt;/span&gt; Styler.SLDManager(&lt;span style="color: blue;"&gt;this&lt;/span&gt;.map, {'proxyUrl': &lt;span style="color: blue;"&gt;this&lt;/span&gt;.proxyUrl});&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;18:   ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;19: }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;20: ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;21: &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;22: &lt;span style="color: green;"&gt;// Note: the original constructors for SLDManager and SchemaManager takes a single input parameter&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;23: &lt;span style="color: green;"&gt;// but they will be modified to take 2nd input parameters in SLDManager.js and SchemaManager.js&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;24: &lt;/pre&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Changes in SLDManager.js&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;a. Add an extra parameter called “proxyUrl” right before the constructor&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 768px;"&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: &lt;span style="color: green;"&gt;// right before the “constructor”, and after other properties&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2: ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3: proxyUrl: "&lt;span style="color: darkred;"&gt;&lt;/span&gt;",&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4: ...&lt;/pre&gt;&lt;/pre&gt;&lt;br /&gt;b. Modify SLDManager constructor to take 2nd input parameter&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 768px;"&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: initialize: &lt;span style="color: blue;"&gt;function&lt;/span&gt;(map, options) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2:     &lt;span style="color: blue;"&gt;this&lt;/span&gt;.map = map;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:     &lt;span style="color: blue;"&gt;var&lt;/span&gt; layer;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:     &lt;span style="color: blue;"&gt;this&lt;/span&gt;.layers = [];&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:     &lt;span style="color: blue;"&gt;this&lt;/span&gt;.layerData = {};&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6:     &lt;span style="color: blue;"&gt;this&lt;/span&gt;.format = &lt;span style="color: blue;"&gt;new&lt;/span&gt; OpenLayers.Format.SLD({multipleSymbolizers: &lt;span style="color: blue;"&gt;true&lt;/span&gt;});&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7:     &lt;span style="color: blue;"&gt;for&lt;/span&gt; (&lt;span style="color: blue;"&gt;var&lt;/span&gt; i=0; i&amp;lt;&lt;span style="color: blue;"&gt;this&lt;/span&gt;.map.layers.&lt;span style="color: blue;"&gt;length&lt;/span&gt;; ++i) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8:         layer = &lt;span style="color: blue;"&gt;this&lt;/span&gt;.map.layers[i];&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9:         &lt;span style="color: blue;"&gt;if&lt;/span&gt;(layer &lt;span style="color: blue;"&gt;instanceof&lt;/span&gt; OpenLayers.Layer.WMS) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;10:             &lt;span style="color: blue;"&gt;this&lt;/span&gt;.layers.push(layer);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;11:         }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;12:     }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;13:     &lt;span style="color: green;"&gt;// this is important so user can pass in ‘proxyUrl’&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: yellow; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;14:     OpenLayers.Util.extend(&lt;span style="color: blue;"&gt;this&lt;/span&gt;, options);  &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;15: },&lt;/pre&gt;&lt;/pre&gt;&lt;br /&gt;c. Overwrite getUrl() so it sends GetStyle request to retrieve WMS SLD defintion, here is an example&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 768px;"&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: getUrl: &lt;span style="color: blue;"&gt;function&lt;/span&gt;(layer, styleName) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2:     &lt;span style="color: blue;"&gt;var&lt;/span&gt; url;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:     &lt;span style="color: blue;"&gt;if&lt;/span&gt;(layer &lt;span style="color: blue;"&gt;instanceof&lt;/span&gt; OpenLayers.Layer.WMS) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: yellow; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:         url = layer.url.split("&lt;span style="color: darkred;"&gt;?&lt;/span&gt;")[0] + "&lt;span style="color: darkred;"&gt;?&lt;/span&gt;" + "&lt;span style="color: darkred;"&gt;version=1.3.0&amp;amp;request=GetStyles&amp;amp;layers=&lt;/span&gt;" + layer.params["&lt;span style="color: darkred;"&gt;LAYERS&lt;/span&gt;"];       &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:         OpenLayers.Console.log(url);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6:     }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7:     &lt;span style="color: green;"&gt;//TODO handle other layer types&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8:     &lt;span style="color: blue;"&gt;return&lt;/span&gt; url;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9: }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;10: &lt;/pre&gt;&lt;/pre&gt;&lt;br /&gt;d. In loadSld() method replace&amp;nbsp; this.getUrl(layer, styleName) with this.proxyUrl + this.getUrl(layer, styleName)&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 768px;"&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2: loadSld: &lt;span style="color: blue;"&gt;function&lt;/span&gt;(layer, styleName, callback) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:         Ext.Ajax.request({&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:             url: &lt;span style="color: blue;"&gt;this&lt;/span&gt;.proxyUrl + &lt;span style="color: blue;"&gt;this&lt;/span&gt;.getUrl(layer, styleName),&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:             method: "&lt;span style="color: darkred;"&gt;GET&lt;/span&gt;",&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6:             …              &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7: ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8: &lt;span style="color: green;"&gt;// obviously there may be more than one styles for each WMS layer, &lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9: &lt;span style="color: green;"&gt;// in this case the first style is always used.&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;10: &lt;/pre&gt;&lt;/pre&gt;&lt;br /&gt;e. In saveSld() method, replace original code with following to use “SLD_BODY” parameter to make stateless change of rendering and symbology&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 768px;"&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: saveSld: &lt;span style="color: blue;"&gt;function&lt;/span&gt;(layer, callback, scope) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2:     &lt;span style="color: blue;"&gt;this&lt;/span&gt;.layerData[layer.id].style.&lt;span style="color: blue;"&gt;name&lt;/span&gt; = "&lt;span style="color: darkred;"&gt;__internalstyle__&lt;/span&gt;";&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:         &lt;span style="color: blue;"&gt;var&lt;/span&gt; sldBody = &lt;span style="color: blue;"&gt;this&lt;/span&gt;.format.write(&lt;span style="color: blue;"&gt;this&lt;/span&gt;.layerData[layer.id].sld, {});&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:     layer.mergeNewParams({&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:       styles: "&lt;span style="color: darkred;"&gt;__internalstyle__&lt;/span&gt;", &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6:       sld_body: sldBody&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7:     });&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8:     callback.call(scope || &lt;span style="color: blue;"&gt;this&lt;/span&gt;);  &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9:   }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;10: &lt;span style="color: green;"&gt;// By using “SLD_BODY” any change to the style is stateless which will not affect other users. &lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;11: &lt;span style="color: green;"&gt;// But if you want to persist modified styles on server side, &lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;12: &lt;span style="color: green;"&gt;// you must use WMS PutStyle operation and also make sure server supports it&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;13: &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;14: &lt;/pre&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Changes in SchemaManager.js&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;a. Add two extra parameters called “wfsUrl” and “proxyUrl” right before the constructor&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 768px;"&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: &lt;span style="color: green;"&gt;// right before the “constructor”, and after other properties&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2: ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3: proxyUrl: "&lt;span style="color: darkred;"&gt;&lt;/span&gt;",&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4: wfsUrl: “”,&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5: ...&lt;/pre&gt;&lt;/pre&gt;&lt;br /&gt;b. Modify SchemaManager constructor to take 2nd input parameter&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 768px;"&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: initialize: &lt;span style="color: blue;"&gt;function&lt;/span&gt;(map, options) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2:             &lt;span style="color: green;"&gt;// other code&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3: ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:             OpenLayers.Util.extend(&lt;span style="color: blue;"&gt;this&lt;/span&gt;, options);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:        ...     &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6:             &lt;span style="color: blue;"&gt;for&lt;/span&gt;(&lt;span style="color: blue;"&gt;var&lt;/span&gt; i=0; i&amp;lt;&lt;span style="color: blue;"&gt;this&lt;/span&gt;.map.layers.&lt;span style="color: blue;"&gt;length&lt;/span&gt;; ++i) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7:                   layer = &lt;span style="color: blue;"&gt;this&lt;/span&gt;.map.layers[i];&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8:                   &lt;span style="color: blue;"&gt;if&lt;/span&gt;(layer &lt;span style="color: blue;"&gt;instanceof&lt;/span&gt; OpenLayers.Layer.WMS) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9:                         &lt;span style="color: blue;"&gt;this&lt;/span&gt;.attributeStores[layer.id] = &lt;span style="color: blue;"&gt;new&lt;/span&gt; GeoExt.data.AttributeStore({&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;10:                               &lt;span style="color: green;"&gt;//url: layer.url.split("?")[0].replace("/wms", "/wfs"),&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;11:                           &lt;span style="color: green;"&gt;// TODO: need better logic to handle case where this.wfsUrl is empty or must be derived from this.wmsUrl&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: yellow; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;12:                           url: &lt;span style="color: blue;"&gt;this&lt;/span&gt;.proxyUrl + &lt;span style="color: blue;"&gt;this&lt;/span&gt;.wfsUrl, &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;13:                               baseParams: {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;14:                                     &lt;span style="color: green;"&gt;//version: "1.1.1",    // is there a WFS 1.1.1?&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;15:                             version: "&lt;span style="color: darkred;"&gt;1.1.0&lt;/span&gt;",&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;16:                                     request: "&lt;span style="color: darkred;"&gt;DescribeFeatureType&lt;/span&gt;",&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;17:                                     typename: layer.params["&lt;span style="color: darkred;"&gt;LAYERS&lt;/span&gt;"]&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;18:                               }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;19:                         });&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;20:                   }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;21:             }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;22:       }&lt;/pre&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;5.&lt;/b&gt; Rebuild Styler and test against a generic WMS server.&lt;br /&gt;&lt;br /&gt;So far the OpenGeo Styler has been hacked to communicate with a generic WMS server. To test it you just need to pass in the urls for WMS, WFS and proxy into Styler constructor in the original index.html like below.&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 768px;"&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: Ext.BLANK_IMAGE_URL = "&lt;span style="color: darkred;"&gt;theme/img/blank.gif&lt;/span&gt;";&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2: Ext.onReady(&lt;span style="color: blue;"&gt;function&lt;/span&gt;() {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:     &lt;span style="color: blue;"&gt;window&lt;/span&gt;.styler = &lt;span style="color: blue;"&gt;new&lt;/span&gt; Styler({&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:         baseLayers: [                              &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:             &lt;span style="color: blue;"&gt;new&lt;/span&gt; OpenLayers.Layer.OSM(&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6:                 "&lt;span style="color: darkred;"&gt;Open Street Map&lt;/span&gt;",&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7:                 "&lt;span style="color: darkred;"&gt;http://tile.openstreetmap.org/${z}/${x}/${y}.png&lt;/span&gt;",&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8:                 {numZoomLevels: 19}&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9:             )                                                             &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;10:         ],                        &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: yellow; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;11:         wmsUrl:"&lt;span style="color: darkred;"&gt;&lt;a href="http://localhost:8399/%3CWMSServer_endpoint%3E?%22,"&gt;http://localhost:8399/&amp;lt;WMSServer_endpoint&amp;gt;?&lt;/a&gt;&lt;/span&gt;&lt;a href="http://localhost:8399/%3CWMSServer_endpoint%3E?%22,"&gt;",&lt;br /&gt;&lt;/a&gt;&lt;/pre&gt;&lt;pre style="background-color: yellow; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;12:         wfsUrl:"&lt;span style="color: darkred;"&gt;&lt;a href="http://localhost:8399/%3CWFSServer_endpoint%3E?%22,"&gt;http://localhost:8399/&amp;lt;WFSServer_endpoint&amp;gt;?&lt;/a&gt;&lt;/span&gt;&lt;a href="http://localhost:8399/%3CWFSServer_endpoint%3E?%22,"&gt;",&lt;br /&gt;&lt;/a&gt;&lt;/pre&gt;&lt;pre style="background-color: yellow; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;13:         proxyUrl: "&lt;span style="color: darkred;"&gt;&lt;a href="http://locahost:8080/openlayers-trunk/ApacheProxyServlet?url=%22"&gt;http://locahost:8080/openlayers-trunk/ApacheProxyServlet?url=&lt;/a&gt;&lt;/span&gt;&lt;a href="http://locahost:8080/openlayers-trunk/ApacheProxyServlet?url=%22"&gt;"&lt;br /&gt;&lt;/a&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;14:     });&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;15: });&lt;/pre&gt;&lt;/pre&gt;&lt;br /&gt;Some screenshots:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://lh6.ggpht.com/_qgvb6YVf5Fw/TOhbvbUkDZI/AAAAAAAAARA/QkAi7lFYkOE/s1600-h/image%5B3%5D.png"&gt;&lt;img alt="image" border="0" height="798" src="http://lh4.ggpht.com/_qgvb6YVf5Fw/TOhbv7H8eHI/AAAAAAAAARE/6teJ2U1mFvM/image_thumb%5B1%5D.png?imgmax=800" style="border: 0px none; display: inline;" title="image" width="974" /&gt;&lt;/a&gt; &lt;br /&gt;Styler load SLD style definition through WMS GetStyle operation&lt;br /&gt;&lt;br /&gt;&lt;a href="http://lh6.ggpht.com/_qgvb6YVf5Fw/TOhbw7KW-JI/AAAAAAAAARI/ZfTDPAJ3m2g/s1600-h/image%5B12%5D.png"&gt;&lt;img alt="image" border="0" height="945" src="http://lh4.ggpht.com/_qgvb6YVf5Fw/TOhbxdFu-5I/AAAAAAAAARM/8xNcYS0Um1A/image_thumb%5B8%5D.png?imgmax=800" style="border: 0px none; display: inline;" title="image" width="1045" /&gt;&lt;/a&gt; &lt;br /&gt;Styler changes map symbology through SLD_BODY parameter &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;6. Things to improve&lt;br /&gt;&lt;ul&gt;&lt;li&gt;If a layer has more than one styles, provide GUI for users to select&amp;nbsp; &lt;/li&gt;&lt;li&gt;Make OpenGeo Styler to persist SLD styles on server through WMS PutStyle operation&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4314202365991499343-6798418622159492654?l=augusttown.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://augusttown.blogspot.com/feeds/6798418622159492654/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://augusttown.blogspot.com/2010/11/hack-opengeo-styler-to-work-with.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/6798418622159492654'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/6798418622159492654'/><link rel='alternate' type='text/html' href='http://augusttown.blogspot.com/2010/11/hack-opengeo-styler-to-work-with.html' title='Hack OpenGeo Styler to work with generic WMS servers'/><author><name>Yingqi Tang</name><uri>http://www.blogger.com/profile/00422288991527433117</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/--JG8BCe4IgI/Tja7MbVf-uI/AAAAAAAAAYQ/nde6VIRt3aY/s220/august_and_me.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_qgvb6YVf5Fw/TOhbv7H8eHI/AAAAAAAAARE/6teJ2U1mFvM/s72-c/image_thumb%5B1%5D.png?imgmax=800' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4314202365991499343.post-671911375406611858</id><published>2010-10-21T00:12:00.001-07:00</published><updated>2010-11-22T10:22:33.604-08:00</updated><title type='text'>Using USGS High Resolution Orthoimagery in JOSM</title><content type='html'>One of CarbonCloud’s &lt;a href="http://carboncloud.blogspot.com/2010/09/usgs-seamless-map-service-gets-rid-of.html"&gt;recent article&lt;/a&gt; pointed me to &lt;a href="http://seamless.usgs.gov/"&gt;USGS Seamless Data&lt;/a&gt; Warehouse, where many high resolution orthoimagery data is distributed as &lt;a href="http://seamless.usgs.gov/wms.php"&gt;OGC WMS&lt;/a&gt;. It’s a perfect base map option for OSM contributors using JOSM because of the following reasons:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The super image quality and resolution (0.25 foot to 2.5 foot);&lt;/li&gt;&lt;li&gt;Distributed through open standard based interface (WMS), which can be directly used in JOSM;&lt;/li&gt;&lt;li&gt;Compatibility with OSM license makes it legal to be used to derive OSM data on. (features digitized based on Google Maps and Bings are actually considered as derivative work of them, which then is copyrighted)&lt;/li&gt;&lt;/ul&gt;An obvious disadvantage though is the data coverage because only very limited area are covered by those orthoimagery data. See &lt;a href="http://seamless.usgs.gov/wms_data_availability.php?id=1"&gt;data availability&lt;/a&gt;.&lt;br /&gt;Here are some basic steps to use USGS high resolution orthoimagery in JOSM:&lt;br /&gt;1. Install &lt;a href="http://josm.openstreetmap.de/wiki/InstallNotes"&gt;JOSM&lt;/a&gt; and &lt;a href="http://wiki.openstreetmap.org/wiki/JOSM/Plugins/WMSPlugin"&gt;WMS plugin&lt;/a&gt;&lt;br /&gt;2. Find the WMS containing the orthoimagery data that covers the area you’re interested in&lt;br /&gt;Here is the list of available WMS services and you can probably tell the covered area by service name. For example, I am interested in making some OSM edits in Redlands, CA, United States, so I pick the WMS &lt;a href="http://imsortho.cr.usgs.gov/wmsconnector/com.esri.wms.Esrimap/USGS_EDC_Ortho_California?REQUEST=capabilities&amp;amp;SERVICE=WMS"&gt;USGS_EDC_Ortho_California&lt;/a&gt;, in which there is particular layer for Riverside and San Bernardino counties.&lt;br /&gt;&lt;a href="http://lh6.ggpht.com/_qgvb6YVf5Fw/TL_nza1Bh8I/AAAAAAAAAQU/CfIizII05eA/s1600-h/Untitled%5B4%5D.png"&gt;&lt;img alt="Untitled" border="0" height="146" src="http://lh4.ggpht.com/_qgvb6YVf5Fw/TL_nzsGm_mI/AAAAAAAAAQY/h1BtjD6TZFM/Untitled_thumb%5B2%5D.png?imgmax=800" style="border: 0px none; display: inline;" title="Untitled" width="1000" /&gt;&lt;/a&gt; &lt;br /&gt;3. Start JOSM and load the WMS layer in JOSM WMS plugin&lt;br /&gt;After you download the data from OSM in JOSM, you need to register WMS &lt;a href="http://imsortho.cr.usgs.gov/wmsconnector/com.esri.wms.Esrimap/USGS_EDC_Ortho_California?REQUEST=capabilities&amp;amp;SERVICE=WMS"&gt;USGS_EDC_Ortho_California&lt;/a&gt; in JOSM through WMS Plugin Preference. So just go to Edit—&amp;gt;Preferences—&amp;gt;WMS tab;&lt;br /&gt;&lt;a href="http://lh5.ggpht.com/_qgvb6YVf5Fw/TL_n0QKo3HI/AAAAAAAAAQc/s-SLUIIWweo/s1600-h/image%5B4%5D.png"&gt;&lt;img alt="image" border="0" height="639" src="http://lh5.ggpht.com/_qgvb6YVf5Fw/TL_n1XVwVXI/AAAAAAAAAQg/-cB6TEfJ_rs/image_thumb%5B2%5D.png?imgmax=800" style="border: 0px none; display: inline;" title="image" width="1028" /&gt;&lt;/a&gt; &lt;br /&gt;Click “Add”, and type in a name and url as part of a WMS GetMap request&lt;br /&gt;&lt;a href="http://lh5.ggpht.com/_qgvb6YVf5Fw/TL_n1r1_lBI/AAAAAAAAAQk/TFqk1LRTMYg/s1600-h/image%5B8%5D.png"&gt;&lt;img alt="image" border="0" height="180" src="http://lh3.ggpht.com/_qgvb6YVf5Fw/TL_n112E9xI/AAAAAAAAAQo/46CkHRP4Fvw/image_thumb%5B4%5D.png?imgmax=800" style="border: 0px none; display: inline;" title="image" width="644" /&gt;&lt;/a&gt; &lt;br /&gt;It’s a little tricky for WMS URL because you need to append a few GetMap request parameters manually. It seems to me that JOSM WMS plugin will only automatically populate “BBOX” parameter so you must manually complete other request parameters. Here is the URL that I came up with:&lt;br /&gt;&lt;a href="http://imsortho.cr.usgs.gov/wmsconnector/com.esri.wms.Esrimap/USGS_EDC_Ortho_California?VERSION=1.1.1&amp;amp;REQUEST=GetMap&amp;amp;SRS=EPSG:4326&amp;amp;LAYERS=Riverside-SanBernardinoCA_0.3m_Color_Mar_2008&amp;amp;STYLES=&amp;amp;EXCEPTIONS=application/vnd.ogc.se_xml&amp;amp;FORMAT=image/png&amp;amp;BGCOLOR=0xFEFFFF&amp;amp;TRANSPARENT=TRUE&amp;amp;" title="http://imsortho.cr.usgs.gov/wmsconnector/com.esri.wms.Esrimap/USGS_EDC_Ortho_California?VERSION=1.1.1&amp;amp;REQUEST=GetMap&amp;amp;SRS=EPSG:4326&amp;amp;WIDTH=975&amp;amp;HEIGHT=804&amp;amp;LAYERS=Riverside-SanBernardinoCA_0.3m_Color_Mar_2008&amp;amp;STYLES=&amp;amp;EXCEPTIONS=application/vnd.ogc.se_xml&amp;amp;FORMAT=image/png&amp;amp;BGCOLOR=0xFEFFFF&amp;amp;TRANSPARENT=TRUE&amp;amp;"&gt;http://imsortho.cr.usgs.gov/wmsconnector/com.esri.wms.Esrimap/USGS_EDC_Ortho_California?VERSION=1.1.1&amp;amp;REQUEST=GetMap&amp;amp;SRS=EPSG:4326&amp;amp;LAYERS=Riverside-SanBernardinoCA_0.3m_Color_Mar_2008&amp;amp;STYLES=&amp;amp;EXCEPTIONS=application/vnd.ogc.se_xml&amp;amp;FORMAT=image/png&amp;amp;BGCOLOR=0xFEFFFF&amp;amp;TRANSPARENT=TRUE&amp;amp;&lt;/a&gt;&lt;br /&gt;Notice that I indicate the layer “Riverside-SanBernardinoCA_0.3m_Color_Mar_2008”, which is the area I am interested in.&lt;br /&gt;&lt;i&gt;Note: you should request WMS map in WGS84 (EPSG:4326) because the OSM data overlay on it are all in longitude and latitude.&lt;/i&gt;&lt;br /&gt;That’s it, enjoy the armchair mapping! &lt;br /&gt;Below is a snapshot of JOSM with high resolution orthoimagery as base map&lt;br /&gt;&lt;a href="http://lh3.ggpht.com/_qgvb6YVf5Fw/TL_n3EdvV8I/AAAAAAAAAQs/HsEVOisGkOE/s1600-h/image%5B20%5D.png"&gt;&lt;img alt="image" border="0" height="726" src="http://lh3.ggpht.com/_qgvb6YVf5Fw/TL_n6-wAcoI/AAAAAAAAAQw/2e5UGGU0KJU/image_thumb%5B10%5D.png?imgmax=800" style="border: 0px none; display: inline;" title="image" width="1028" /&gt;&lt;/a&gt; &lt;br /&gt;Same area with Yahoo Imagery as a comparison&lt;br /&gt;&lt;a href="http://lh6.ggpht.com/_qgvb6YVf5Fw/TL_n8QoB_wI/AAAAAAAAAQ0/WxPnpZbFlQE/s1600-h/image%5B16%5D.png"&gt;&lt;img alt="image" border="0" height="728" src="http://lh3.ggpht.com/_qgvb6YVf5Fw/TL_n99Z0x8I/AAAAAAAAAQ4/cBMRQeu9pBg/image_thumb%5B8%5D.png?imgmax=800" style="border: 0px none; display: inline;" title="image" width="1028" /&gt;&lt;/a&gt; &lt;br /&gt;Is that amazing! Only a little issue of JOSM WMS plugin itself though: if you zoom in after loading the WMS image you need to delete and add the same WMS again to refresh data from WMS, otherwise the image at previous zoom level will simply be resample and used.&lt;br /&gt;&lt;br /&gt;This &lt;a href="http://wiki.openstreetmap.org/wiki/USGS_High_Resolution_Orthoimagery"&gt;OSM wiki page&lt;/a&gt; has a lot of useful information regarding using USGS High Resolution Orthoimagery in JOSM&lt;span class="Apple-style-span" style="border-collapse: separate; color: black; font-family: sans-serif; font-size: 16px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"&gt;&lt;span class="Apple-style-span" style="line-height: 24px;"&gt;&lt;/span&gt;&lt;/span&gt; too.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4314202365991499343-671911375406611858?l=augusttown.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://augusttown.blogspot.com/feeds/671911375406611858/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://augusttown.blogspot.com/2010/10/using-usgs-high-resolution-orthoimagery.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/671911375406611858'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/671911375406611858'/><link rel='alternate' type='text/html' href='http://augusttown.blogspot.com/2010/10/using-usgs-high-resolution-orthoimagery.html' title='Using USGS High Resolution Orthoimagery in JOSM'/><author><name>Yingqi Tang</name><uri>http://www.blogger.com/profile/00422288991527433117</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/--JG8BCe4IgI/Tja7MbVf-uI/AAAAAAAAAYQ/nde6VIRt3aY/s220/august_and_me.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_qgvb6YVf5Fw/TL_nzsGm_mI/AAAAAAAAAQY/h1BtjD6TZFM/s72-c/Untitled_thumb%5B2%5D.png?imgmax=800' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4314202365991499343.post-8925226625046949611</id><published>2010-10-13T00:45:00.001-07:00</published><updated>2010-10-13T00:49:02.169-07:00</updated><title type='text'>IP-based geolocation can also be accurate</title><content type='html'>I always thought that IP-based geolocation is not as accurate as it should be until quite recently I ran into this &lt;a href="http://html5demos.com/geo"&gt;HTML5 demo page&lt;/a&gt; (Geolocation API is one of the new features in HTML5), and now I have to admit that it was my prejudice.&lt;br /&gt;If you open &lt;a href="http://html5demos.com/geo"&gt;the demo page&lt;/a&gt; in browser and assume you don’t have a GPS device connected to your machine, then all this web app does is to detect your location based on your IP address and add a maker on Google Maps. The source code is also as simple as a “HelloWorld” sample of W3C Geolocation API:&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 768px;"&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;&lt;span style="color: blue;"&gt;if&lt;/span&gt; (navigator.geolocation) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;navigator.geolocation.getCurrentPosition(success, error);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;} &lt;span style="color: blue;"&gt;else&lt;/span&gt; {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;error('not supported');&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;&lt;span style="color: blue;"&gt;function&lt;/span&gt; success(position) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;&lt;span style="color: green;"&gt;// ...non-related code omitted...&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;&lt;span style="color: blue;"&gt;var&lt;/span&gt; latlng = &lt;span style="color: blue;"&gt;new&lt;/span&gt; google.maps.LatLng(position.coords.latitude, position.coords.longitude);  &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;&lt;span style="color: blue;"&gt;var&lt;/span&gt; map = &lt;span style="color: blue;"&gt;new&lt;/span&gt; google.maps.Map(&lt;span style="color: blue;"&gt;document&lt;/span&gt;.getElementById("&lt;span style="color: darkred;"&gt;mapcanvas&lt;/span&gt;"), myOptions);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;&lt;span style="color: blue;"&gt;var&lt;/span&gt; marker = &lt;span style="color: blue;"&gt;new&lt;/span&gt; google.maps.Marker({&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;position: latlng, &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;map: map, &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;title:"&lt;span style="color: darkred;"&gt;You are here!&lt;/span&gt;"&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;});&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;&lt;span style="color: blue;"&gt;function&lt;/span&gt; error(msg) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;&lt;span style="color: green;"&gt;// ...non-related code omitted...&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;&lt;/pre&gt;&lt;pre style="background-color: #e4e4e4; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;&lt;/pre&gt;&lt;/pre&gt;&lt;br /&gt;I tried this geolocation demo app twice at my bedroom, one using Firefox 3.6 and the other is using Google Chrome, and below are how the app locates me:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://lh3.ggpht.com/_qgvb6YVf5Fw/TLVjgAIxAmI/AAAAAAAAAP4/spAUXXx27JA/s1600-h/Untitled%5B6%5D.png"&gt;&lt;img alt="Untitled" border="0" height="572" src="http://lh3.ggpht.com/_qgvb6YVf5Fw/TLVjg_uW0RI/AAAAAAAAAP8/Kc7r_LmxaBs/Untitled_thumb%5B4%5D.png?imgmax=800" style="border: 0px none; display: inline;" title="Untitled" width="644" /&gt;&lt;/a&gt; &lt;br /&gt;Firefox 3.6 Successfully locate me in my land lot&lt;br /&gt;&lt;br /&gt;&lt;a href="http://lh6.ggpht.com/_qgvb6YVf5Fw/TLVkRDuspBI/AAAAAAAAAQM/NtRcBImj_Ns/s1600-h/Untitled%5B10%5D.png"&gt;&lt;img alt="Untitled" border="0" height="575" src="http://lh6.ggpht.com/_qgvb6YVf5Fw/TLVkRoqi6CI/AAAAAAAAAQQ/wfUBvm1FUqI/Untitled_thumb%5B6%5D.png?imgmax=800" style="border: 0px none; display: inline;" title="Untitled" width="644" /&gt;&lt;/a&gt; &lt;br /&gt;Google Chrome is a little off my land lot, but still quite accurate&lt;br /&gt;&lt;br /&gt;The results of both are very accurate to where I actually was. That’s just amazing! And I guess the conclusion is that IP-based Geolocation can also be really accurate.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4314202365991499343-8925226625046949611?l=augusttown.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://augusttown.blogspot.com/feeds/8925226625046949611/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://augusttown.blogspot.com/2010/10/ip-based-geolocation-can-also-be.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/8925226625046949611'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/8925226625046949611'/><link rel='alternate' type='text/html' href='http://augusttown.blogspot.com/2010/10/ip-based-geolocation-can-also-be.html' title='IP-based geolocation can also be accurate'/><author><name>Yingqi Tang</name><uri>http://www.blogger.com/profile/00422288991527433117</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/--JG8BCe4IgI/Tja7MbVf-uI/AAAAAAAAAYQ/nde6VIRt3aY/s220/august_and_me.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_qgvb6YVf5Fw/TLVjg_uW0RI/AAAAAAAAAP8/Kc7r_LmxaBs/s72-c/Untitled_thumb%5B4%5D.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4314202365991499343.post-9186475224494362790</id><published>2010-08-19T22:19:00.001-07:00</published><updated>2010-08-19T22:19:45.010-07:00</updated><title type='text'>NASA Earth Observation (NEO) Website is awesome</title><content type='html'>&lt;p&gt;I recently came cross this &lt;a href="http://neo.sci.gsfc.nasa.gov/Search.html"&gt;NEO (NASA Earth Observation)&lt;/a&gt; website that contains a lot of recent and historical satellite image data related to environment and climate change. Unlike most other websites of the same type, NEO’s simple and pretty web GUI design and user experience really surprises me.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_qgvb6YVf5Fw/TG4QaY_69WI/AAAAAAAAAPg/DfCxY9mZ4qs/s1600-h/neo%5B3%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="neo" border="0" alt="neo" src="http://lh6.ggpht.com/_qgvb6YVf5Fw/TG4QbA-DIeI/AAAAAAAAAPk/LQfVItLl7mw/neo_thumb%5B1%5D.png?imgmax=800" width="753" height="925" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Screenshot taken from NASA Earth Observation site at &lt;a href="http://neo.sci.gsfc.nasa.gov/Search.html"&gt;http://neo.sci.gsfc.nasa.gov/Search.html&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;The dataset hosted by NEO are divided into categories like ocean, atmosphere , energy, land and life. For each of those categories users can browse, search, preview, download, and analysis which is pretty much all you may want to do with satellite data. What’s really awesome is that you can do all of those things in a single ajax-based web page (you see only one screenshot is enough). &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Discover data&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;To find the data, you can select one of the main categories, and then either search or look through the full list. Once it’s found you will be able to see the brief description and preview it in an interactive web map (support zoom in/out)&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Download option&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Most of the data can be downloaded as PNG, JPEG, GeoTiff, CSV, KML/KMZ directly through NEO home page, and whenever applicable you can also download data in original format e.g. HDF on the same page of data search result.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Web Map Service&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;NEO also provides WMS access to its data, which is a super plus for those who don't want to download and host data themselves.&amp;#160; NEO’s WMS links:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://neowms.sci.gsfc.nasa.gov/wms/wms?version=1.1.1&amp;amp;service=WMS&amp;amp;request=GetCapabilities"&gt;http://neowms.sci.gsfc.nasa.gov/wms/wms?version=1.1.1&amp;amp;service=WMS&amp;amp;request=GetCapabilities&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://neowms.sci.gsfc.nasa.gov/wms/wms?version=1.3.0&amp;amp;service=WMS&amp;amp;request=GetCapabilities"&gt;http://neowms.sci.gsfc.nasa.gov/wms/wms?version=1.3.0&amp;amp;service=WMS&amp;amp;request=GetCapabilities&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_qgvb6YVf5Fw/TG4QbavlyLI/AAAAAAAAAPo/mQI10nNNvYk/s1600-h/image%5B5%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_qgvb6YVf5Fw/TG4QcNOw9CI/AAAAAAAAAPs/nBV6hIEdnSw/image_thumb%5B3%5D.png?imgmax=800" width="644" height="395" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Consume NEO WMS in QGIS&lt;/p&gt;  &lt;p&gt;NASA Earth Observation site is just awesome!!!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4314202365991499343-9186475224494362790?l=augusttown.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://augusttown.blogspot.com/feeds/9186475224494362790/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://augusttown.blogspot.com/2010/08/nasa-earth-observation-neo-website-is.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/9186475224494362790'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/9186475224494362790'/><link rel='alternate' type='text/html' href='http://augusttown.blogspot.com/2010/08/nasa-earth-observation-neo-website-is.html' title='NASA Earth Observation (NEO) Website is awesome'/><author><name>Yingqi Tang</name><uri>http://www.blogger.com/profile/00422288991527433117</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/--JG8BCe4IgI/Tja7MbVf-uI/AAAAAAAAAYQ/nde6VIRt3aY/s220/august_and_me.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_qgvb6YVf5Fw/TG4QbA-DIeI/AAAAAAAAAPk/LQfVItLl7mw/s72-c/neo_thumb%5B1%5D.png?imgmax=800' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4314202365991499343.post-5063190780181552438</id><published>2010-08-11T17:14:00.001-07:00</published><updated>2010-08-11T17:14:21.781-07:00</updated><title type='text'>Mysterious “BBOX” parameter in Web Feature Service (WFS)</title><content type='html'>&lt;p&gt;“BBOX” is a standard parameter defined as part of the OGC Web Feature Service (WFS) keyword-value encoding which is used in GetFeature requests to request features within a specific extent. So “…&amp;amp;BBOX=-180.0,-90.0,180.0,90.0…” means getting all the features in the extent defined by lower corner coordinate (-180.0,-90.0) and upper corner coordinate (180.0,90.0). Not as straight forward as it seems to be to most people, I actually find it quite confusing when trying to answer this question below:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;em&gt;Is it possible to set BBOX to coordinate values in other projection?&lt;/em&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;The answer to this question depends on whether it’s WFS 1.0.0 (No) or WFS 1.1.0 (Yes), and the yes for WFS 1.1.0 even introduces some more complexity. So in the rest of this article I am going to clear things up based on the definition and statement in following OGC specifications:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;a href="http://portal.opengeospatial.org/files/?artifact_id=7176"&gt;OGC Web Feature Service (WFS) Implementation Specification 1.0.0 (02-058)&lt;/a&gt; &lt;/li&gt;    &lt;li&gt;&lt;a href="http://portal.opengeospatial.org/files/?artifact_id=8339"&gt;OGC Web Feature Service (WFS) Implementation Specification 1.1.0 (04-094)&lt;/a&gt; &lt;/li&gt;    &lt;li&gt;&lt;a href="http://www.google.com/url?sa=t&amp;amp;source=web&amp;amp;cd=3&amp;amp;ved=0CBgQFjAC&amp;amp;url=http%3A%2F%2Fportal.opengeospatial.org%2Ffiles%2F%3Fartifact_id%3D20040&amp;amp;ei=_jljTOq4LcKC8gbCwL2CCg&amp;amp;usg=AFQjCNFPLtN0Lyd9RnQaOr_v2I7RaWQxag"&gt;OGC Web Service Common (OWS) Implementation Specification 1.1.0 (06-121r3)&lt;/a&gt; &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;u&gt;BBOX in WFS 1.0.0&lt;/u&gt;&lt;/p&gt;  &lt;p&gt;First of all, it is a little less confusing for WFS version 1.0.0 that&amp;#160; you CAN’T specify BBOX in coordinates of any other projections. This is based on two facts in specs: &lt;/p&gt;  &lt;p&gt;(1) WFS 1.0.0 spec (02-058), section 13.3.3, where it says:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;em&gt;The SRS of the bounding box must be the same as the SRS of the feature type(s) in a request. The SRS of a feature type is advertised by a WFS in the capabilities document. If more than one feature type is specified in a request, the feature types must all be in the same SRS and the BBOX must be specified in the common SRS as well.&lt;/em&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;(2) In &lt;a href="http://schemas.opengis.net/wfs/1.0.0/WFS-basic.xsd"&gt;WFS 1.0.0 schema&lt;/a&gt;, a WFS’s capabilities response can have only one “SRS” allowed for each feature type.&lt;/p&gt;  &lt;p&gt;But I did see people get confused and thus argue about one thing: Can I specify BBOX in EPSG:4326?&lt;/p&gt;  &lt;p&gt;Based on the statement above, if a WFS serves out feature types in a non-EPSG4326 then it should be valid and compliant to just reject GetFeature request with BBOX in EPSG:4326. But another paragraph in WFS 1.0.0 spec (same section 13.3.3) seems to indicate a yes on it:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;em&gt;If a request contains a Bounding Box whose area does not overlap at all with the LatLongBoundingBox(s) advertised in the Capabilities XML for the requested geodata object, the server should return empty content (e.g. null feature set) for that element. Any elements that are partly or entirely contained in the Bounding Box should be returned in the appropriate format.&lt;/em&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;In this case we don’t really have to strictly follow the spec, and I won’t be surprised to see most WFS 1.0.0 implementations supports BBOX in EPSG:4326 no matter what SRS its feature types are in.&lt;/p&gt;  &lt;p&gt;&lt;u&gt;BBOX in WFS 1.1.0&lt;/u&gt;&lt;/p&gt;  &lt;p&gt;Now for WFS 1.1.0, things get a little complicated because of the fact that there is an optional “crsurl” value you can append at the end of the BBOX coordinates to explicitly specify what coordinate reference system the BBOX is in. For example, ”…&amp;amp;BBOX=minX,minY,maxX,maxY,EPSG:900913” means that those min/max X/Y values are in EPSG:900913. Another complexity is that in WFS 1.1.0 capabilities response there can be multiple supported SRS for each feature type.&amp;#160; So based the section 14.3.3 of WFS spec 1.1.0:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;em&gt;The KVP encoding for a bounding box is defined in subclause 10.2.3 of normative reference [15]. The general form of the parameter is:        &lt;br /&gt;BBOX=lcc1,lcc2,…,lccN,ucc1,ucc2,…uccN[,crsuri]…&lt;/em&gt;&lt;/p&gt;    &lt;p&gt;&lt;em&gt;       &lt;br /&gt;…If the crsuri is not specified then the 2-D coordinates shall be specified using decimal degrees and WGS84 as described in [15].&lt;/em&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Note: The WFS spec is reference OWS 1.1.0 spec as [15] here&lt;/p&gt;  &lt;p&gt;Here I can actually get two conclusions:&lt;/p&gt;  &lt;p&gt;(1) You can specify BBOX in any coordinate reference system as long as you provide a “crsuri”.&lt;/p&gt;  &lt;p&gt;Although not described in spec, I would believe you will get correct and useful response only if you request one of those supported coordinate reference system listed in a WFS’s capabilities.&lt;/p&gt;  &lt;p&gt;(2) If “crsuri” is missing, then the coordinate values specified in BBOX should be considered as WGS84 as described in OWS 1.1.0 spec as below:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;em&gt;A WGS 84 bounding box shall be KVP encoded in a corresponding parameter value list, with the ordered listed values for the quantities:        &lt;br /&gt;LowerCorner longitude, in decimal degrees         &lt;br /&gt;LowerCorner latitude, in decimal degrees         &lt;br /&gt;UpperCorner longitude, in decimal degrees         &lt;br /&gt;UpperCorner latitude, in decimal degrees         &lt;br /&gt;crs URI = “urn:ogc:def:crs:OGC:1.3:CRS84” (optional)&lt;/em&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;In another word, if you set BBOX parameter in request without a “crsurl”, the BBOX value should always be in syntax “min_lon, min_lat, max_lon, max_lat”.&lt;/p&gt;  &lt;p&gt;&lt;u&gt;EPSG:4326 versus. CRS84&lt;/u&gt;&lt;/p&gt;  &lt;p&gt;Now one finally thing people get confused very often is the order of longitude and latitude they should put in BBOX parameter for EPSG:4326 and CRS:84. Originally and as always, official EPSG:4326 defines coordinates in order of “latitude” followed by “longitude”, but unfortunately almost everyone speaks and uses the reverse order as “longitude” followed by “latitude”, which is why old spec like WMS 1.1.1 and WFS 1.0.0 also uses this order for EPSG:4326. But in most recent specifications OGC is trying to fix this common mistaken usage of EPSG:4326 by creating a brand new CRS:84, which based on my understanding is identical to EPSG:4326 except that it defines coordinates in order of “longitude” followed by “latitude”. So as far as WFS goes, always follow the rules below:&lt;/p&gt;  &lt;p&gt;For WFS 1.0.0, uses EPSG:4326 and always set BBOX coordinate values as “longitude” followed by “latitude”. For WFS 1.1.0, specify coordinates in BBOX as longitude followed by latitude if you’re setting CRS:84 as the coordinate reference system for BBOX (e.g. “…&amp;amp;BBOX=–180.0,-90.0,180.0,90.0,urn:ogc:def:crs:OGC:1.3:CRS84…”), or set it the other way if you’re setting EPSG:4326 for BBOX (e.g. “…&amp;amp;BBOX=-90.0,-180.0,90.0,180.0,urn:x-ogc:def:crs:EPSG:6.9:4326…), but again if you’re omittng “crsuri” at the end please always stick to the order where longitude is ahead of latitude.&lt;/p&gt;  &lt;p&gt;&lt;u&gt;Summary&lt;/u&gt;&lt;/p&gt;  &lt;p&gt;“BBOX” parameter looks easy and straight forward to use, but it also gets complicated when different versions of WFS implementations and EPSG:4326/WGS84 are involved. Hopefully this article clear things up a little.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4314202365991499343-5063190780181552438?l=augusttown.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://augusttown.blogspot.com/feeds/5063190780181552438/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://augusttown.blogspot.com/2010/08/mysterious-bbox-parameter-in-web.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/5063190780181552438'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/5063190780181552438'/><link rel='alternate' type='text/html' href='http://augusttown.blogspot.com/2010/08/mysterious-bbox-parameter-in-web.html' title='Mysterious “BBOX” parameter in Web Feature Service (WFS)'/><author><name>Yingqi Tang</name><uri>http://www.blogger.com/profile/00422288991527433117</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/--JG8BCe4IgI/Tja7MbVf-uI/AAAAAAAAAYQ/nde6VIRt3aY/s220/august_and_me.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4314202365991499343.post-494540703373246791</id><published>2010-05-03T23:13:00.001-07:00</published><updated>2010-05-03T23:16:34.941-07:00</updated><title type='text'>Expo 2010 Shanghai China</title><content type='html'>To celebrate the opening of Expo 2010 Shanghai China, I just had a peek at where the Expo Axis should locate in Shanghai in those popular online map services (Google Maps, OSM, Bing Maps, and Yahoo Maps). See what I get:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;OSM Maps&lt;/b&gt;&lt;br /&gt;&lt;a href="http://lh3.ggpht.com/_qgvb6YVf5Fw/S9-6ycsui5I/AAAAAAAAAI0/1HyPECNy72c/s1600-h/osm%5B4%5D.png"&gt;&lt;img alt="osm" border="0" height="661" src="http://lh6.ggpht.com/_qgvb6YVf5Fw/S9-60E3-EKI/AAAAAAAAAI4/ncO2QOusi8o/osm_thumb%5B2%5D.png?imgmax=800" style="border: 0px none; display: inline;" title="osm" width="1028" /&gt;&lt;/a&gt; &lt;br /&gt;Not bad, but that is actually what I expected to see.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Google Maps&lt;/b&gt;&lt;br /&gt;&lt;a href="http://lh6.ggpht.com/_qgvb6YVf5Fw/S9-60yWHG8I/AAAAAAAAAI8/XdGxT5uVtJ0/s1600-h/google%5B3%5D.png"&gt;&lt;img alt="google" border="0" height="591" src="http://lh5.ggpht.com/_qgvb6YVf5Fw/S9-62AiBNMI/AAAAAAAAAJA/nvzSDWWwy0Y/google_thumb%5B1%5D.png?imgmax=800" style="border: 0px none; display: inline;" title="google" width="1028" /&gt;&lt;/a&gt; &lt;br /&gt;It seems like Google Maps really needs to catch up with this big event. But if you’re laughing at Google Maps now, you better check out Bing Maps and Yahoo Maps below.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Bing Maps&lt;/b&gt;&lt;br /&gt;&lt;a href="http://lh6.ggpht.com/_qgvb6YVf5Fw/S9-62j5IqDI/AAAAAAAAAJE/YqFthNFA3PM/s1600-h/bing%5B3%5D.png"&gt;&lt;img alt="bing" border="0" height="597" src="http://lh4.ggpht.com/_qgvb6YVf5Fw/S9-629MVz9I/AAAAAAAAAJI/TjaYIzqbS1c/bing_thumb%5B1%5D.png?imgmax=800" style="border: 0px none; display: inline;" title="bing" width="1028" /&gt;&lt;/a&gt; &lt;br /&gt;What can I say…just like the map…&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Yahoo Maps&lt;/b&gt;&lt;br /&gt;&lt;a href="http://lh4.ggpht.com/_qgvb6YVf5Fw/S9-7CZLjq9I/AAAAAAAAAJM/UD5lN4Z4X4c/s1600-h/yahoo%5B3%5D.png"&gt;&lt;img alt="yahoo" border="0" height="557" src="http://lh3.ggpht.com/_qgvb6YVf5Fw/S9-7CzjMjoI/AAAAAAAAAJQ/PvJGSPsaGkA/yahoo_thumb%5B1%5D.png?imgmax=800" style="border: 0px none; display: inline;" title="yahoo" width="1028" /&gt;&lt;/a&gt; &lt;br /&gt;You know what? I zoomed out on purpose because I can’t zoom in any more…&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4314202365991499343-494540703373246791?l=augusttown.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://augusttown.blogspot.com/feeds/494540703373246791/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://augusttown.blogspot.com/2010/05/expo-2010-shanghai-china.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/494540703373246791'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/494540703373246791'/><link rel='alternate' type='text/html' href='http://augusttown.blogspot.com/2010/05/expo-2010-shanghai-china.html' title='Expo 2010 Shanghai China'/><author><name>Yingqi Tang</name><uri>http://www.blogger.com/profile/00422288991527433117</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/--JG8BCe4IgI/Tja7MbVf-uI/AAAAAAAAAYQ/nde6VIRt3aY/s220/august_and_me.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_qgvb6YVf5Fw/S9-60E3-EKI/AAAAAAAAAI4/ncO2QOusi8o/s72-c/osm_thumb%5B2%5D.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4314202365991499343.post-4553100417289370902</id><published>2010-04-14T17:23:00.001-07:00</published><updated>2010-04-15T08:06:32.436-07:00</updated><title type='text'>Several approaches to do Geolocation by IP address programmatically</title><content type='html'>&lt;a href="http://en.wikipedia.org/wiki/Geolocation"&gt;Geolocation&lt;/a&gt; is the process of identifying the real-world geographic location based on a device’s IP address (either connected through cable or WiFi access point), GSM/CDMA cells IDs or GPS and so on. The result of Geolocation is essentially a pair of coordinate (lat and lon) on earth, and if combined with other service like Geocoding the result can also be translated to a well-known place name or a physical address. &lt;br /&gt;Geolocation by IP address although&amp;nbsp; may not be as accurate as GPS sometimes, does have great value in many of today’s location aware web applications. One of the typical examples is that when you do search on internet the search engine will detect the location by your IP address and automatically filter and sort the results based on that information. In this article I will be looking at some existing approaches I’ve played with to do Geolocation by IP address.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1. W3C Geolocation API&lt;/b&gt;&lt;br /&gt;If you’re in JavaScript environment, &lt;a href="http://dev.w3.org/geo/api/spec-source.html"&gt;W3C Geolocation API&lt;/a&gt; is definitely the approach you should follow because it is the standard. Independent of the actual source of the location information which is not just limited to IP address but can also be WiFi and Bluetooth MAC address, GSM/CDMA cell IDs, GPS or so, it actually defines a high-level interface to location associated information with devices hosting the implementation. Other than that W3C Geolocation API also defines the way you request repeated position updates through callback mechanism as well as requesting an aged position.&lt;br /&gt;Mozilla Firefox is a very good example of such &lt;a href="https://developer.mozilla.org/En/Using_geolocation"&gt;implementation&lt;/a&gt;. The code example below illustrate the common work flow to get your current location in a JavaScript application:&lt;br /&gt;&lt;pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;&lt;span style="color: blue;"&gt;if&lt;/span&gt;(navigator.geolocation) {  &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;&lt;span style="color: green;"&gt;/* geolocation is available */&lt;/span&gt;  &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;navigator.geolocation.getCurrentPosition(&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;&lt;span style="color: blue;"&gt;function&lt;/span&gt;(position) { &lt;span style="color: green;"&gt;// success callback  &lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;&lt;span style="color: green;"&gt;//alert(position.coords.latitude + "," + position.coords.longitude);&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;&lt;span style="color: blue;"&gt;alert&lt;/span&gt;(position.address.city);                &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;},&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;&lt;span style="color: blue;"&gt;function&lt;/span&gt;() { &lt;span style="color: green;"&gt;// failure callback&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;&lt;span style="color: green;"&gt;// error handling&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;},&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;{&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;&lt;span style="color: green;"&gt;// other options&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;timeout:30000&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;);    &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;} &lt;span style="color: blue;"&gt;else&lt;/span&gt; {      &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;&lt;span style="color: green;"&gt;// browser does not support Geolocation API&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;} &lt;/pre&gt;&lt;/pre&gt;In Firefox’s implementation, it gives you not only the lat and lon value of your location but also gives you the address associated with that coordinate. Under the hood Firefox takes advantage of Google Location Service which I believe provided all necessary infrastructure.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2. Google Geolocation API in Gears &lt;/b&gt;&lt;br /&gt;Google Gears Geolocation API implements the same W3c Geolocation interfaces but in a more device independent, location information source independent, and browser independent way. It’s online doc is very good and concise for people who would like to use it in their web application.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;3. Google Location Service&lt;/b&gt;&lt;br /&gt;Google Location Service is far more than an interfaces that only provides geolocation function, but it’s totally valid to use that as a Geolocation service. Without even realizing, it is definitely behind a lot of its own services like Search and Latitude which sometimes you don’t even realize. It is also backing up many other Geolocation API implementations like Firefox. But unfortunately I was able to find out an explicit native Geolocation service API either in JavaScript or in REST flavor. So it’s probably impossible to directly communication with Google Location Service by feeding an IP address and getting a coordinate back. &lt;br /&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;4. Other Geolocation by IP service providers&lt;/b&gt;&lt;br /&gt;HostIP, mentioned in Andrew Turner’s &lt;a href="http://oreilly.com/catalog/9780596529956"&gt;Introduction to Neogeography&lt;/a&gt;, &lt;a href="http://hostip.info/"&gt;HostIP&lt;/a&gt; has the largest free database and service for GeoIP (the term Andrew uses for geolocation by IP address). The best part of HostIP is that it freely distribute it’s database with regular updates. Since I don’t find any information regarding license and term of use for HostIP database I would assume it is in public domain. Programmatically HostIP provides a simple and lightweight &lt;a href="http://www.hostip.info/use.html"&gt;RESTful API&lt;/a&gt; for people to locate themselves by IP. It doesn’t do much but it’s good at what it does. Because of its REST nature, it is not limited to any program environment except that you might need a proxy in your JavaScript web application. Below is an example:&lt;br /&gt;&lt;pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;http://api.hostip.info/country.php&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;US&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;http://api.hostip.info/get_html.php?ip=12.215.42.19&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;Country: UNITED STATES (US)&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;City: Sugar Grove, IL&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;IP: 12.215.42.19&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;http://api.hostip.info/get_html.php?ip=12.215.42.19&amp;amp;position=true&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;Country: UNITED STATES (US)&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;City: Sugar Grove, IL&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;Latitude: 41.7696&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;Longitude: -88.4588&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;IP: 12.215.42.19&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;http://api.hostip.info/?ip=12.215.42.19&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;[use the URL above for an example - XML too long to paste below]&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;&lt;/pre&gt;&lt;/pre&gt;Finally its &lt;a href="http://www.hostip.info/"&gt;website&lt;/a&gt; itself provides a simple browser based GUI for people who don’t want to bother coding their own app.&lt;br /&gt;&lt;br /&gt;GeoIP: &lt;a href="http://www.maxmind.com/"&gt;MaxMind&lt;/a&gt; has a free GeoIP database and API called &lt;a href="http://www.maxmind.com/app/geolitecity"&gt;GeoLite City&lt;/a&gt;. MaxMind and it GeoIP product suite does not have a publicly hosted RESTful service or API for directly use but it allows you to download their database in either binary format or CSV format (mainly for you to load data into SQL database) to host locally. The API for accessing the local database is available in many platforms (C, C#, MS COM, PHP, Java, Perl Python, Ruby etc.). The workflow is to first download the &lt;a href="http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz"&gt;database&lt;/a&gt; to local disk and uncompress it, then use any of the API available the query the database. Below is an example I tried out using its Java API:&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 650px;"&gt;&lt;pre style="background-color: #fbfbfb; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;&lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;void&lt;/span&gt; lookupServiceTest() {  &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;&lt;span style="color: blue;"&gt;try&lt;/span&gt; {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #fbfbfb; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;LookupService cl = &lt;span style="color: blue;"&gt;new&lt;/span&gt; LookupService("&lt;span style="color: darkred;"&gt;E:\\projects\\geoip\\geolite-city-database\\GeoLiteCity.dat&lt;/span&gt;", LookupService.GEOIP_MEMORY_CACHE);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;Location location = cl.getLocation("xxx&lt;span style="color: darkred;"&gt;.xxx.xxx.xxx&lt;/span&gt;");&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #fbfbfb; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;System.out.println("&lt;span style="color: darkred;"&gt; countryCode: &lt;/span&gt;" + location.countryCode +&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #fbfbfb; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;"&lt;span style="color: darkred;"&gt;\n countryName: &lt;/span&gt;" + location.countryName +&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;"&lt;span style="color: darkred;"&gt;\n region: &lt;/span&gt;" + location.region +&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #fbfbfb; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;"&lt;span style="color: darkred;"&gt;\n regionName: &lt;/span&gt;" + regionName.regionNameByCode(location.countryCode, location.region) +&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;"&lt;span style="color: darkred;"&gt;\n city: &lt;/span&gt;" + location.city +&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #fbfbfb; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;"&lt;span style="color: darkred;"&gt;\n postalCode: &lt;/span&gt;" + location.postalCode +&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;"&lt;span style="color: darkred;"&gt;\n latitude: &lt;/span&gt;" + location.latitude +&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #fbfbfb; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;"&lt;span style="color: darkred;"&gt;\n longitude: &lt;/span&gt;" + location.longitude +&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;"&lt;span style="color: darkred;"&gt;\n metro code: &lt;/span&gt;" + location.metro_code +&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #fbfbfb; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;"&lt;span style="color: darkred;"&gt;\n area code: &lt;/span&gt;" + location.area_code +&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;"&lt;span style="color: darkred;"&gt;\n timezone: &lt;/span&gt;" + timeZone.timeZoneByCountryAndRegion(location.countryCode, location.region));&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #fbfbfb; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;cl.close();&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;} &lt;span style="color: blue;"&gt;catch&lt;/span&gt; (IOException e) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #fbfbfb; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;System.out.println("&lt;span style="color: darkred;"&gt;IO Exception&lt;/span&gt;");&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;}  &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #fbfbfb; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;}&lt;/pre&gt;&lt;/pre&gt;GeoIP API is free and open source under GPL/LGPL license, and the GeoLite City database itself is also free under its &lt;a href="http://geolite.maxmind.com/download/geoip/database/LICENSE.txt"&gt;database license&lt;/a&gt;. MaxMind also provides an advanced commercial version called &lt;a href="http://www.maxmind.com/app/city"&gt;GeoIP City&lt;/a&gt;, which is functionally equal to the free one but has more accuracy, different redistribution license and more frequent updates.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4314202365991499343-4553100417289370902?l=augusttown.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://augusttown.blogspot.com/feeds/4553100417289370902/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://augusttown.blogspot.com/2010/04/several-approaches-to-programmatically.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/4553100417289370902'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/4553100417289370902'/><link rel='alternate' type='text/html' href='http://augusttown.blogspot.com/2010/04/several-approaches-to-programmatically.html' title='Several approaches to do Geolocation by IP address programmatically'/><author><name>Yingqi Tang</name><uri>http://www.blogger.com/profile/00422288991527433117</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/--JG8BCe4IgI/Tja7MbVf-uI/AAAAAAAAAYQ/nde6VIRt3aY/s220/august_and_me.jpeg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4314202365991499343.post-7997128458184554664</id><published>2010-03-28T23:07:00.001-07:00</published><updated>2010-03-28T23:08:57.624-07:00</updated><title type='text'>Using Java Map Projection Library in Android</title><content type='html'>Recently I was writing a little mapping application on Android, which basically takes Open Street Map tiles as base layer, and also has the ability to overlay some vector features on top. In this application there is need to convert coordinates back and forth between WGS84 and Mercator projection, so I need certain library to do the job. Given the popularity of &lt;a href="http://trac.osgeo.org/proj/"&gt;PROJ.4&lt;/a&gt;, the pure Java port of it, &lt;a href="http://www.jhlabs.com/java/maps/proj/"&gt;Java Map Projection Library&lt;/a&gt; (name it “PROJ4Java” below), seems to be an obvious choice so I spend some hours during the weekend to see how it works.&lt;br /&gt;First you can get the latest version (1.0.6) of PROJ4Java from this &lt;a href="http://www.jhlabs.com/java/maps/proj/javaproj-1.0.6.zip"&gt;link&lt;/a&gt;. The size of the library is very small and lightweight with only 1.17MB for complete source code and 247KB for compiled jar file. If you’re familiar with PROJ.4 itself then this Java version should be quite straight forward, otherwise there isn’t much sample code or tutorial on its homepage. But the basic workflow is quite simple as described by its project homepage:&lt;br /&gt;&lt;blockquote&gt;&lt;i&gt;To use the library, you need to create a projection, either directly by calling its constructor or via one of the factory methods. Once you have a Projection, you can use it to convert between latitude/longitude and projected map units (by default, metres) by calling the transform and transformInverse methods.&lt;/i&gt;&lt;/blockquote&gt;A major issue of using PROJ4Java on Android is that out of box it can’t be run due to the fact that it is using some classes in java.awt.geom.* which is not available in Android SDK. But since the only major AWT class used by PROJ4Java is Point2D, it’s not difficult at all to work around by replacing it with your own 2D point class. In my case I use the point class from JTS library. After taking out all Java awt class references from source code and replacing them with JTS class, everything works pretty well on Android. &lt;br /&gt;Another thing I modifies on the source code is to get rid of those projection implementations I don’t need. Mercator projection is the only one I need so far so that I am able to reduce the size of compile jar file to only 168KB. Here is a list of classes I extracted from original source code to support only Mercator projection:&lt;br /&gt;&lt;ul&gt;&lt;ul&gt;&lt;li&gt;com.jhlabs.map.*&lt;/li&gt;&lt;li&gt;com.jhlabs.map.proj.CylindricalProjection&lt;/li&gt;&lt;li&gt;com.jhlabs.map.proj.Ellipsoid&lt;/li&gt;&lt;li&gt;com.jhlabs.map.proj.MercatorProjection&lt;/li&gt;&lt;li&gt;com.jhlabs.map.proj.Projection&lt;/li&gt;&lt;li&gt;com.jhlabs.map.proj.ProjectionFactory&lt;/li&gt;&lt;li&gt;com.jhlabs.map.proj.ProjectionException&lt;/li&gt;&lt;li&gt;com.jhlabs.map.proj.Ellipsoid&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;ProjectionFactory class is the entry point for you to use PROJ4Java, which provides several static methods to create an instance of Projection. Below are some use cases I figured out by briefly reading through the source code:&lt;br /&gt;(1) ProjectionFactory.getNamedPROJ4CoordinateSystem(String name) takes a well-known projection code like “epsg:4326” and returns a Projection: &lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: String name = "&lt;span style="color: darkred;"&gt;epsg:3785&lt;/span&gt;";    &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2: Projection proj = ProjectionFactory.getNamedPROJ4CoordinateSystem(name);&lt;/pre&gt;&lt;/pre&gt;(2) ProjectionFactory.fromPROJ4Specification(String[] params) takes a set of PROJ.4 parameters and returns a Projection:&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: String[] params = {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2:     "&lt;span style="color: darkred;"&gt;proj=tmerc&lt;/span&gt;",&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:     "&lt;span style="color: darkred;"&gt;lat_0=37.5&lt;/span&gt;",&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:     "&lt;span style="color: darkred;"&gt;lon_0=-85.66666666666667&lt;/span&gt;",&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:     "&lt;span style="color: darkred;"&gt;k=0.999966667&lt;/span&gt;",&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6:     "&lt;span style="color: darkred;"&gt;x_0=99999.99989839978&lt;/span&gt;",&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7:     "&lt;span style="color: darkred;"&gt;y_0=249999.9998983998&lt;/span&gt;",&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8:     "&lt;span style="color: darkred;"&gt;ellps=GRS80&lt;/span&gt;",&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9:     "&lt;span style="color: darkred;"&gt;datum=NAD83&lt;/span&gt;",&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;10:     "&lt;span style="color: darkred;"&gt;to_meter=0.3048006096012192&lt;/span&gt;",&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;11:     "&lt;span style="color: darkred;"&gt;no_defs&lt;/span&gt;"&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;12: };&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;13: ProjectionFactory.fromPROJ4Specification(params);&lt;/pre&gt;&lt;/pre&gt;(3) Create an instance of customized projection. All supported projection definition strings (PROJ.4 parameters for ) for PROJ4Java are listed in text files under folder “nad” that is coming with the library. These files are “epsg”, “esri”, “nad27”, “nad83”, and “world”, and a sample projection definition string is like below:&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: &amp;lt;2965&amp;gt; +proj=tmerc +lat_0=37.5 +lon_0=-85.66666666666667 +k=0.999966667 +x_0=99999.99989839978 +y_0=249999.9998983998 +ellps=GRS80 +datum=NAD83 +to_meter=0.3048006096012192 +no_defs&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2: &lt;/pre&gt;&lt;/pre&gt;To add a new customized projection just create a definition string (usually modify certain parameter values upon an existing projection) and add it into one of those files, or create a new text file under “nad” folder with the definition string, then use the code below:&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: Projection proj = &lt;span style="color: blue;"&gt;null&lt;/span&gt;;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2: &lt;span style="color: blue;"&gt;try&lt;/span&gt; {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:     &lt;span style="color: green;"&gt;// assume that "epsg:900913" projection defintion &lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:     &lt;span style="color: green;"&gt;//   has been added in text file "others"&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:     proj = ProjectionFactory.readProjectionFile("&lt;span style="color: darkred;"&gt;others&lt;/span&gt;", "&lt;span style="color: darkred;"&gt;900913&lt;/span&gt;");    &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6: } &lt;span style="color: blue;"&gt;catch&lt;/span&gt;(IOException e) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7:     e.printStackTrace();&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8: }&lt;/pre&gt;&lt;/pre&gt;(4) Transform between lat/lon and projection units with a Projection instance: &lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: Projection epsg3785 = ProjectionFactory.getNamedPROJ4CoordinateSystem("&lt;span style="color: darkred;"&gt;epsg:3785&lt;/span&gt;");    &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2:               &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3: System.out.println("&lt;span style="color: darkred;"&gt;transform from latlon to epsg:3785&lt;/span&gt;");&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4: System.out.println("&lt;span style="color: darkred;"&gt;latlon: -117.5931084, 34.1063989&lt;/span&gt;");&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5: Point pEpsg3785 = epsg3785.transform(-117.5931084, 34.1063989);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6: System.out.println("&lt;span style="color: darkred;"&gt;epsg:3785: &lt;/span&gt;" + pEpsg3785.getX() + "&lt;span style="color: darkred;"&gt;, &lt;/span&gt;" + pEpsg3785.getY());&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7:     &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8: System.out.println("&lt;span style="color: darkred;"&gt;transform from epsg:3785 to latlon&lt;/span&gt;");&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9: System.out.println("&lt;span style="color: darkred;"&gt;epsg:3785: &lt;/span&gt;" + pEpsg3785.getX() + "&lt;span style="color: darkred;"&gt;, &lt;/span&gt;" + pEpsg3785.getY());&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;10: Point latlon = epsg3785.inverseTransform(pEpsg3785);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;11: System.out.println("&lt;span style="color: darkred;"&gt;latlon: &lt;/span&gt;" + latlon.getX() + "&lt;span style="color: darkred;"&gt;, &lt;/span&gt;" + latlon.getY());&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;12: &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;13:   &lt;/pre&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4314202365991499343-7997128458184554664?l=augusttown.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://augusttown.blogspot.com/feeds/7997128458184554664/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://augusttown.blogspot.com/2010/03/using-java-map-projection-library-in.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/7997128458184554664'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/7997128458184554664'/><link rel='alternate' type='text/html' href='http://augusttown.blogspot.com/2010/03/using-java-map-projection-library-in.html' title='Using Java Map Projection Library in Android'/><author><name>Yingqi Tang</name><uri>http://www.blogger.com/profile/00422288991527433117</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/--JG8BCe4IgI/Tja7MbVf-uI/AAAAAAAAAYQ/nde6VIRt3aY/s220/august_and_me.jpeg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4314202365991499343.post-246461813642342781</id><published>2010-03-09T13:02:00.001-08:00</published><updated>2010-03-09T13:04:33.363-08:00</updated><title type='text'>WMS TIME support in MapServer</title><content type='html'>In context of my &lt;a href="http://augusttown.blogspot.com/2010/03/two-ambiguities-about-time-in-ogc-wms.html"&gt;last blog&lt;/a&gt;, I’m looking at the WMS TIME support in &lt;a href="http://mapserver.org/"&gt;OSGeo MapServer&lt;/a&gt;. MapServer has a very concise and helpful &lt;a href="http://mapserver.org/ogc/wms_time.html"&gt;doc&lt;/a&gt; on the scope and usage of WMS TIME in its implementation. It is always a good idea to briefly go through the documentation first before you spend a lot time trying something that may not be even&amp;nbsp; implemented.&lt;br /&gt;My test data (in shapefile format) is very simple such that the picture below tells all about it:&lt;br /&gt;&lt;a href="http://lh5.ggpht.com/_qgvb6YVf5Fw/S5a3crqgW_I/AAAAAAAAAIs/rhk8q6szWFs/s1600-h/05.png"&gt;&lt;img alt="0" border="0" height="533" src="http://lh5.ggpht.com/_qgvb6YVf5Fw/S5a3dn1MW7I/AAAAAAAAAIw/yKPKo4aNYDo/0_thumb3.png?imgmax=800" style="border-width: 0px; display: inline;" title="0" width="682" /&gt;&lt;/a&gt; &lt;br /&gt;Each point feature has a field “TIME” which has time value in syntax “YYYY-MM-DD HH:MM:SS”. This is valid ISO8601 time format and supported by MapServer. All points have a time stamp within day 2010-02-16. To enable time on WMS in MapServer is straight forward given that you have some experience of publishing shapefile as a WMS in MapServer. The only extra thing you need to configure is to specify time field, time extent and default time value in map file. That’s it and MapServer documentation is very clear on how to do that.&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: &lt;span style="color: maroon;"&gt;LAYER&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2:     &lt;span style="color: maroon;"&gt;NAME&lt;/span&gt; '&lt;span style="color: maroon;"&gt;points&lt;/span&gt;'&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:     &lt;span style="color: maroon;"&gt;METADATA&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:         "&lt;span style="color: maroon;"&gt;ows&lt;/span&gt;_&lt;span style="color: maroon;"&gt;title&lt;/span&gt;" "&lt;span style="color: maroon;"&gt;points&lt;/span&gt;"&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:         "&lt;span style="color: maroon;"&gt;gml&lt;/span&gt;_&lt;span style="color: maroon;"&gt;include&lt;/span&gt;_&lt;span style="color: maroon;"&gt;items&lt;/span&gt;"   "&lt;span style="color: maroon;"&gt;all&lt;/span&gt;"&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6:         "&lt;span style="color: maroon;"&gt;wms&lt;/span&gt;_&lt;span style="color: maroon;"&gt;include&lt;/span&gt;_&lt;span style="color: maroon;"&gt;items&lt;/span&gt;"   "&lt;span style="color: maroon;"&gt;all&lt;/span&gt;"&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7:         &lt;span style="color: maroon;"&gt;#&lt;/span&gt; &lt;span style="color: maroon;"&gt;TIME&lt;/span&gt; &lt;span style="color: maroon;"&gt;Related&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: yellow; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8:         "&lt;span style="color: maroon;"&gt;wms&lt;/span&gt;_&lt;span style="color: maroon;"&gt;title&lt;/span&gt;"          "&lt;span style="color: maroon;"&gt;points&lt;/span&gt;"&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: yellow; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9:         "&lt;span style="color: maroon;"&gt;wms&lt;/span&gt;_&lt;span style="color: maroon;"&gt;timeextent&lt;/span&gt;"     "&lt;span style="color: maroon;"&gt;2010-02-16T00&lt;/span&gt;:&lt;span style="color: maroon;"&gt;00&lt;/span&gt;:&lt;span style="color: maroon;"&gt;00Z&lt;/span&gt;/&lt;span style="color: maroon;"&gt;2010-02-17T00&lt;/span&gt;:&lt;span style="color: maroon;"&gt;00&lt;/span&gt;:&lt;span style="color: maroon;"&gt;00Z&lt;/span&gt;/&lt;span style="color: maroon;"&gt;P1H&lt;/span&gt;"&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: yellow; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;10:         "&lt;span style="color: maroon;"&gt;wms&lt;/span&gt;_&lt;span style="color: maroon;"&gt;timeitem&lt;/span&gt;"       "&lt;span style="color: maroon;"&gt;TIME&lt;/span&gt;"&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: yellow; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;11:         "&lt;span style="color: maroon;"&gt;wms&lt;/span&gt;_&lt;span style="color: maroon;"&gt;timedefault&lt;/span&gt;"     "&lt;span style="color: maroon;"&gt;2010-02-16T12&lt;/span&gt;:&lt;span style="color: maroon;"&gt;00&lt;/span&gt;:&lt;span style="color: maroon;"&gt;00Z&lt;/span&gt;,&lt;span style="color: maroon;"&gt;2010-02-16T18&lt;/span&gt;:&lt;span style="color: maroon;"&gt;00&lt;/span&gt;:&lt;span style="color: maroon;"&gt;00Z&lt;/span&gt;"&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;12:     &lt;span style="color: maroon;"&gt;END&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;13: ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;14: &lt;span style="color: maroon;"&gt;END&lt;/span&gt;&lt;/pre&gt;&lt;/pre&gt;After saving the map file and refresh the GetCapabilities response you will notice information of time dimension is advertised.&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;Dimension&lt;/span&gt; &lt;span style="color: red;"&gt;name&lt;/span&gt;=&lt;span style="color: blue;"&gt;"time"&lt;/span&gt; &lt;span style="color: red;"&gt;units&lt;/span&gt;=&lt;span style="color: blue;"&gt;"ISO8601"&lt;/span&gt; &lt;span style="color: red;"&gt;default&lt;/span&gt;=&lt;span style="color: blue;"&gt;"2010-02-16T12:00:00Z,2010-02-16T18:00:00Z"&lt;/span&gt; &lt;span style="color: red;"&gt;nearestValue&lt;/span&gt;=&lt;span style="color: blue;"&gt;"0"&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2:     2010-02-16T00:00:00Z/2010-02-17T00:00:00Z/P1H&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3: &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;Dimension&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4: &lt;/pre&gt;&lt;/pre&gt;The time extent value in &amp;lt;Dimension&amp;gt; is whatever you specified in “wms_timeextent” so it doesn’t smart enough to calculate the extent from data, and neither does MapServer do any validation on the time extent string. A trick I did in map file is to provide a special default time value “2010-02-16T12:00:00Z, 2010-02-16T18:00:00Z,”, which will inform me if certain time string is not correctly processed by MapServer thus default value is used.&lt;br /&gt;&lt;br /&gt;Now it’s time to try a few requests against this time enabled WMS. According to doc the supported request syntax for time are “t”, “t1,t2…”, “t1/t2”, and “t1/t2,t3/t4…” and “t” should be any valid ISO8601 time string. I skipped the time zone because there is nowhere indicating it is implemented (you can not configure time zone at server-side thus it makes no sense to request from client). Below is a list of different time values that have been tried:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;…&amp;amp;TIME=2010 (not recognized, default time value is returned) &lt;/li&gt;&lt;li&gt;…&amp;amp;TIME=2010-02 (not recognized, default time value is returned) &lt;/li&gt;&lt;li&gt;…&amp;amp;TIME=2010-02-16 (recognized, only point of “2010-02-16 00:00:00” returned) &lt;/li&gt;&lt;li&gt;…&amp;amp;TIME=2010-02-16T12 (recognized, only point of “2010-02-16 12:00:00” returned) &lt;/li&gt;&lt;li&gt;…&amp;amp;TIME=2010-02-16T12:00 (recognized, only point of “2010-02-16 12:00:00” returned) &lt;/li&gt;&lt;li&gt;…&amp;amp;TIME=2010-02-16T12:00:00 (recognized, only point of “2010-02-16 12:00:00” returned) &lt;/li&gt;&lt;li&gt;…&amp;amp;TIME=2010-02-16T14:59:59 (recognized, only point of “2010-02-16 14:59:59” returned) &lt;/li&gt;&lt;li&gt;…&amp;amp;TIME=2010-02-16T15:00:01 (recognized, only point of “2010-02-16 15:00:01” returned) &lt;/li&gt;&lt;li&gt;…&amp;amp;TIME=2010-02-16T15:00:00.000 (recognized, only point of “2010-02-16 15:00:00” returned) &lt;/li&gt;&lt;li&gt;…&amp;amp;TIME=2009/2010 (not recognized, default time value is returned) &lt;/li&gt;&lt;li&gt;…&amp;amp;TIME=2010/2011 (not recognized, default time value is returned) &lt;/li&gt;&lt;li&gt;…&amp;amp;TIME=2010-02/2010-03 (not recognized, default time value is returned) &lt;/li&gt;&lt;li&gt;…&amp;amp;TIME=2010-02-16/2010-02-17 (recognized, all points returned) &lt;/li&gt;&lt;/ul&gt;The test results above actually proves the time interpretation described in the documentation:&lt;br /&gt;&lt;blockquote&gt;&lt;i&gt;When MapServer receives a request with a TIME parameter, it transforms the time requests into valid expressions that are assigned to the filter parameter on layers that are time-aware.&lt;/i&gt; &lt;/blockquote&gt;&lt;blockquote&gt;&lt;i&gt;Here are some examples of how different types of requests are treated (wms_timeitem is defined here as being “time_field”):&lt;/i&gt;&lt;br /&gt;&lt;i&gt;single value (2004-10-12) transforms to (`[time_field]` eq `2004-10-12`) &lt;br /&gt;multiple values (2004-10-12, 2004-10-13) transform to (`[time_field]` eq `2004-10-12` OR `[time_field]` eq `2004-10-13`) &lt;br /&gt;single range : 2004-10-12/2004-10-13 transforms to ((`[time_field]` ge `2004-10-12`) AND (`[time_field]` le `2004-10-13`)) &lt;br /&gt;multiple ranges (2004-10-12/2004-10-13, 2004-10-15/2004-10-16) transform to ((`[time_field]` ge `2004-10-12` AND `[time_field]` le `2004-10-13`) OR (`[time_field]` ge `2004-10-15` AND `[time_field]` le `2004-10-16`))&lt;/i&gt; &lt;/blockquote&gt;&lt;blockquote&gt;&lt;i&gt;As shown in the above examples, all fields and values are written inside back tics (`) - this is the general way of specifying time expressions inside MapServer.&lt;/i&gt;&lt;/blockquote&gt;Overall it’s doing a good job and the only thing missing point is that requests like “TIME=2010” and “TIME=2010/2011” should be properly handled.&lt;br /&gt;&lt;br /&gt;If you read carefully, MapServer documentation has another slightly different time interpretation for PostGIS layer, to test that I followed up and applied the same requests on a PostGIS layer.&lt;br /&gt;&lt;br /&gt;Things I need to configure before I can finally send the same set of WMS time requests to PostGIS layer are listed below:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Use “shp2pgsql” command line to convert shape file into pgsql file;&lt;/li&gt;&lt;li&gt;Use “psql” command line to load pgsql file into PostGIS database;&lt;/li&gt;&lt;li&gt;Add another new column of type “timestamp without time zone”;&lt;/li&gt;&lt;li&gt;Copy time value from original column to the new column;&lt;/li&gt;&lt;li&gt;Modify map file to use new column of of type “timestamp without time zone” for WMS time;&lt;/li&gt;&lt;li&gt;Modify the map file to point to PostGIS table instead of shape file;&lt;/li&gt;&lt;/ul&gt;All of above are straight forward except that a WMS layer based on PostGIS doesn’t seem to recognize the temporal field if it is of type “character varying”, which force me to add an extra column in type “timestamp without time zone” and copy over those original time values in string. After all of those it worked and below are the results of same WMS TIME requests on shapefile layer:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;…&amp;amp;TIME=2010 (not recognized, default time value is returned) &lt;/li&gt;&lt;li&gt;…&amp;amp;TIME=2010-02 (not recognized, default time value is returned) &lt;/li&gt;&lt;li&gt;…&amp;amp;TIME=2010-02-16 (recognized, all points with time stamp within 2010-02-16 returned) &lt;/li&gt;&lt;li&gt;…&amp;amp;TIME=2010-02-16T12 (recognized, points between 12:00:00 and 13:00:00 on 2010-02-16 returned, not including points on 13:00:00) &lt;/li&gt;&lt;li&gt;…&amp;amp;TIME=2010-02-16T12:00 (recognized, only point of “2010-02-16 12:00:00” returned) &lt;/li&gt;&lt;li&gt;…&amp;amp;TIME=2010-02-16T12:00:00 (recognized, only point of “2010-02-16 12:00:00” returned) &lt;/li&gt;&lt;li&gt;…&amp;amp;TIME=2010-02-16T14:59:59 (recognized, only point of “2010-02-16 14:59:59” returned) &lt;/li&gt;&lt;li&gt;…&amp;amp;TIME=2010-02-16T15:00:01 (recognized, only point of “2010-02-16 15:00:01” returned) &lt;/li&gt;&lt;li&gt;…&amp;amp;TIME=2010-02-16T15:00:00.000 (recognized, only point of “2010-02-16 15:00:00” returned) &lt;/li&gt;&lt;li&gt;…&amp;amp;TIME=2009/2010 (not recognized, default time value is returned) &lt;/li&gt;&lt;li&gt;…&amp;amp;TIME=2010/2011 (not recognized, default time value is returned) &lt;/li&gt;&lt;li&gt;…&amp;amp;TIME=2010-02/2010-03 (not recognized, default time value is returned) &lt;/li&gt;&lt;li&gt;…&amp;amp;TIME=2010-02-16/2010-02-17 (recognized, all points returned) &lt;/li&gt;&lt;/ul&gt;So it proves what it says in the doc about PostGIS layer:&lt;br /&gt;&lt;blockquote&gt;&lt;i&gt;For &lt;/i&gt;&lt;a href="http://mapserver.org/input/vector/postgis.html#input-postgis"&gt;&lt;i&gt;PostGIS/PostgreSQL&lt;/i&gt;&lt;/a&gt;&lt;i&gt; layers, the time expression built uses the date_trunc function available in PostgreSQL. For example, if the user passes a time value of ‘2004-10-12’, the expression set in the filter is date_trunc(‘day’, time_field) = ‘2004-10-12’. The use of the date_trunc function allows requests to use the concept of time resolution. In the example above, for a request of ‘2004-10-12’, MapServer determines that the resolution is “day” by parsing the time string and the result gives all records matching the date 2004-10-12 regardless of the values set for Hours/Minutes/Seconds in the database. For more information on the date_trunc function, please refer to the &lt;/i&gt;&lt;a href="http://www.postgresql.org/docs/8.1/static/functions-datetime.html"&gt;&lt;i&gt;PostgreSQL documentation&lt;/i&gt;&lt;/a&gt;&lt;i&gt;.&lt;/i&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4314202365991499343-246461813642342781?l=augusttown.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://augusttown.blogspot.com/feeds/246461813642342781/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://augusttown.blogspot.com/2010/03/wms-time-support-in-mapserver.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/246461813642342781'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/246461813642342781'/><link rel='alternate' type='text/html' href='http://augusttown.blogspot.com/2010/03/wms-time-support-in-mapserver.html' title='WMS TIME support in MapServer'/><author><name>Yingqi Tang</name><uri>http://www.blogger.com/profile/00422288991527433117</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/--JG8BCe4IgI/Tja7MbVf-uI/AAAAAAAAAYQ/nde6VIRt3aY/s220/august_and_me.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_qgvb6YVf5Fw/S5a3dn1MW7I/AAAAAAAAAIw/yKPKo4aNYDo/s72-c/0_thumb3.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4314202365991499343.post-1190524131352433286</id><published>2010-03-04T16:13:00.001-08:00</published><updated>2010-03-04T16:13:13.728-08:00</updated><title type='text'>Two ambiguities about TIME in OGC WMS specification</title><content type='html'>&lt;p&gt;OGC WMS specification had included “TIME” dimension since version 1.1.0, in which it guides the WMS implementations on how “TIME” is involved in filtering feature that will finally be visible in either a result map or a query result (aka. GetFeatureInfo result). To me, what it defines about “TIME” dimension in WMS spec seems to be both accurate and ambiguous. It is accurate because it introduces the ISO 8601 standard to cover the exchange of date and time related parameters between a WMS server and a WMS client, which eliminate the risk of misinterpretation where numeric representation of dates and times are interchanged across national boundaries, and to avoid the confusion and other consequential errors or losses (quoted from wiki ISO8601 entry). But it’s also ambiguous in two places (1) where an interpretation is needed for an ISO standard time string; (2) where a temporal attribute has different meanings for different types of features. In this blog let’s skip the accurate part that I am happy with, but discuss the ambiguous part using a few examples.&amp;#160; &lt;/p&gt;  &lt;p&gt;The first ambiguity is actually not a problem of WMS spec itself but comes from the nature of the data when a temporal attribute value has different meanings for different types of features. Basically time values can ne divided into two different types: time as a individual time point and time as a time window.&amp;#160; Time as a time point doesn’t have any relation to its history or future, one good example is a rocket feature with a time attribute called “launch” that has a value of “2000-03-04T00:00:00Z”. It means the rocket launched at Mar.4th 2000 at 12 am which has nothing to do with any time before it or after. But time as a time window instead expand backward or forward. One example is a house feature that has time attribute called “built” with a value of “2000”, it actually affects the time after 2000 because the house exists since then. Another example is an animal species with time attribute “extinct” of value “2000”, which actually affects the time before 2000 because the animal species had existed before 2000.&amp;#160; Given the fact that all of those single time values are treated the same in database, it will potentially affect the result of a query on those features.&lt;/p&gt;  &lt;p&gt;The second ambiguity in WMS spec, as I mentioned above is the interpretation for a time value. For example, when you send a WMS request like “http://…/…request=GETMAP&amp;amp;…&amp;amp;TIME=2010”, what does it exactly mean? It sounds like a stupid question because everyone knows “2010” meaning year 2010, which is clearly defined in ISO8601 as valid. But how do you interpret this request is ambiguous. Assume you’re a time-enabled WMS server and you have some features with time stamps between 2010 and 2011 (any month, day, hour, minute, or second within 2010), will you include those features in the map response to request “…&amp;amp;TIME=2010”? I bet you answer yes without hesitation because naturally “TIME=2010” means within 2010. But will you give it a second thought if I give a different request “http://…/…request=GETMAP&amp;amp;…&amp;amp;TIME=2010-12-10T12” on the same set of features? Do you still think you should include all the features between 12pm and 1pm on 2010-12-10? I don’t think so but that’s actually the same logic you used when you process request “http://…/…request=GETMAP&amp;amp;…&amp;amp;TIME=2010”.&lt;/p&gt;  &lt;p&gt;If you’re implementing a TIME-enabled WMS at server-side you probably already encountered those two problems, and I don’t think the solution is simply throwing the TIME values to database for string comparison even if the database is aware of date/time type. The best way I can think of is always using time window internally (even though sometimes that time window is very small) for both feature temporal attributes and time parameter in the WMS request. To be more specific, when serving out you data if you have a rocket feature launched at “2000-03-04T00:00:00Z” then explicitly treat it as “2000-03-04T00:00:00.000Z/2000-03-04T00:00:00.999Z”; if you have a house built on “2010” then explicitly treat it as “2010/far future”; and finally if you have an animal species extinct in 2000 then explicitly treat it as “long ago/2000”. Now when it comes to time parameter in WMS request, the spec allows syntaxes like “TIME=time1”, “TIME=time_start/time_end”, “TIME=time1,time2” and “TIME=time_start1/time_end1,time_start2/time_end2” but no matter what client sends to you, my suggestion is to always treat each single time value as a time window based on its smallest interval. For example, if “TIME=2000” then treat it as “TIME=2000/2001” (the smallest time interval is year); if “TIME=2000-03-04T00:05Z” treat it as “2000-03-04T00:05Z/2000-03-04T00:06Z” (the smallest time interval is minute) and so on. By using this mechanism every time relate value is a time window, which makes the query more straight forward, just include the feature either in map or query results if the query time window touches, intersects or contains the time window of feature temporal attribute. Although it won’t totally solve the ambiguity above, it helps improve the accuracy.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4314202365991499343-1190524131352433286?l=augusttown.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://augusttown.blogspot.com/feeds/1190524131352433286/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://augusttown.blogspot.com/2010/03/two-ambiguities-about-time-in-ogc-wms.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/1190524131352433286'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/1190524131352433286'/><link rel='alternate' type='text/html' href='http://augusttown.blogspot.com/2010/03/two-ambiguities-about-time-in-ogc-wms.html' title='Two ambiguities about TIME in OGC WMS specification'/><author><name>Yingqi Tang</name><uri>http://www.blogger.com/profile/00422288991527433117</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/--JG8BCe4IgI/Tja7MbVf-uI/AAAAAAAAAYQ/nde6VIRt3aY/s220/august_and_me.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4314202365991499343.post-5071073972865942465</id><published>2010-02-22T13:37:00.001-08:00</published><updated>2010-02-22T14:34:29.577-08:00</updated><title type='text'>Setup a http based SVN repository with TortoiseSVN and Apache http server</title><content type='html'>It is very useful to setup a http based SVN repository on your local machine just for daily check in and out of source code. I am pretty sure there are plenty of materials over the web that explain how to do it for different combinations. Here is the one which I went through and it works pretty good for me. &lt;br /&gt;1. Install and configure TortoiseSVN&lt;br /&gt;&lt;ul&gt;&lt;ul&gt;&lt;li&gt;Download TortoiseSVN from its official website at &lt;a href="http://tortoisesvn.net/downloads" title="http://tortoisesvn.net/downloads"&gt;http://tortoisesvn.net/downloads&lt;/a&gt;; (.msi file) &lt;/li&gt;&lt;li&gt;Install it by following the standard wizards; &lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;2. Install Apache HTTP Server&lt;br /&gt;&lt;ul&gt;&lt;ul&gt;&lt;li&gt;Download Apache HTTP Server from its' website at &lt;a href="http://httpd.apache.org/download.cgi#apache22" title="http://httpd.apache.org/download.cgi#apache22"&gt;http://httpd.apache.org/download.cgi#apache22&lt;/a&gt;, and the version I’m using is 2.2.14 (either the one with or without ssl); try avoid using 1.x or 3.x which either doesn’t work well or hasn’t been tried by many people. So apache 2.2 is the best pick for now; &lt;/li&gt;&lt;li&gt;Install Apache http server by following the wizards; &lt;/li&gt;&lt;li&gt;Change the Apache HTTP Server listening port if necessary in conf. &lt;/li&gt;&lt;li&gt;After installation, just test it by launching &lt;a href="http://localhost:port/"&gt;http://localhost:port/&lt;/a&gt; in browser and you should see a “It works!” page; (you can also monitor the apache http service in Apache Service Monitor)&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;3. Install Subversion&lt;br /&gt;&lt;ul&gt;&lt;ul&gt;&lt;li&gt;Download the latest version of the Subversion Win32 binaries for Apache. Be sure to get the right version to integrate with your version of Apache, otherwise you will get an obscure error message when you try to restart. If you have Apache 2.2.x go to &lt;a href="http://subversion.tigris.org/servlets/ProjectDocumentList?folderID=8100"&gt;http://subversion.tigris.org/servlets/ProjectDocumentList?folderID=8100&lt;/a&gt;;&lt;/li&gt;&lt;li&gt;!!!DON'T!!! use the msi version of subversion (it doesn't work somehow), instead use the zip version; (after unzip the subversion folder to your local disk, you also need to add the bin path to 'path' system environment variable)&lt;/li&gt;&lt;li&gt; &lt;/li&gt;&lt;li&gt;Using the windows explorer, go to the installation directory of Subversion and find the files /httpd/mod_dav_svn.so and mod_authz_svn.so. Copy these files to the Apache modules directory; &lt;/li&gt;&lt;li&gt;Copy the file /bin/libdb*.dll and /bin/intl3_svn.dll from the Subversion installation directory to the Apache bin directory;&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;4. Configure Subversion and Apache HTTP Server&lt;br /&gt;&lt;blockquote&gt;Edit Apache's configuration file (usually C:\Program Files\Apache Group\Apache2\conf\httpd.conf) with a text editor such as Notepad and make the following changes: &lt;/blockquote&gt;&lt;blockquote&gt;Uncomment (remove the '#' mark) the following lines: &lt;/blockquote&gt;&lt;blockquote&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; #LoadModule dav_fs_module modules/mod_dav_fs.so &lt;/span&gt;&lt;/div&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; #LoadModule dav_module modules/mod_dav.so &lt;/span&gt;&lt;/span&gt;&lt;/blockquote&gt;&lt;blockquote&gt;Add the following two lines to the end of the LoadModule section. &lt;/blockquote&gt;&lt;blockquote&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;LoadModule dav_svn_module modules/mod_dav_svn.so &lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; LoadModule authz_svn_module modules/mod_authz_svn.so &lt;/span&gt;&lt;/span&gt;&lt;/blockquote&gt;&lt;blockquote&gt;&lt;b&gt;b.&lt;/b&gt; At the end of the config file add the following lines: &lt;/blockquote&gt;&lt;blockquote&gt;&lt;pre&gt;&lt;span style="font-size: small;"&gt;    &lt;/span&gt;&lt;span style="font-size: small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;lt;Location /svn&amp;gt;&lt;br /&gt;         DAV svn&lt;br /&gt;         SVNListParentPath on&lt;br /&gt;         SVNParentPath D:\SVN&lt;br /&gt;         #SVNIndexXSLT "/svnindex.xsl"&lt;br /&gt;         AuthType Basic&lt;br /&gt;         AuthName "Subversion repositories"&lt;br /&gt;         AuthUserFile passwd&lt;br /&gt;         #AuthzSVNAccessFile svnaccessfile&lt;br /&gt;         Require valid-user&lt;br /&gt;     &amp;lt;/Location&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;c.&lt;/b&gt; This configures Apache so that all your Subversion repositories are physically located below D:\SVN. The repositories are served to the outside world from the URL: http://MyServer/svn/ . Access is restricted to known users/passwords listed in the passwd file. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;d.&lt;/b&gt; To create the passwd file, open the command prompt (DOS-Box) again, change to the apache2 folder (usually c:\program files\apache group\apache2) and create the file by entering &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;bin\htpasswd -c passwd &amp;lt;username&amp;gt; &lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This will create a file with the name passwd which is used for authentication. Additional users can be added with &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;bin\htpasswd passwd &amp;lt;username&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;e.&lt;/b&gt; Restart the Apache service again;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;f.&lt;/b&gt; Point your browser to http://MyServer/svn/MyNewRepository (where MyNewRepository is the name of the Subversion repository you created before). If all went well you should be prompted for a username and password, then you can see the contents of your repository.&lt;/blockquote&gt;5. Create SVN repository and access it through HTTP&lt;br /&gt;&lt;ul&gt;&lt;ul&gt;&lt;li&gt;Create a folder on your local disk (under the folder where you specify in Apache http server to be the root folder of your SVN);&lt;/li&gt;&lt;li&gt;Right click on the folder and use the TortoiseSVN context menu “Create Repository Here”;&lt;/li&gt;&lt;li&gt;Access your SVN repository through HTTP as &lt;a href="http://server:port/svn/repositoryName/"&gt;http://server:port/svn/repositoryName/&lt;/a&gt; (you need to type in username and password).&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4314202365991499343-5071073972865942465?l=augusttown.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://augusttown.blogspot.com/feeds/5071073972865942465/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://augusttown.blogspot.com/2010/02/setup-http-based-svn-repository-with.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/5071073972865942465'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/5071073972865942465'/><link rel='alternate' type='text/html' href='http://augusttown.blogspot.com/2010/02/setup-http-based-svn-repository-with.html' title='Setup a http based SVN repository with TortoiseSVN and Apache http server'/><author><name>Yingqi Tang</name><uri>http://www.blogger.com/profile/00422288991527433117</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/--JG8BCe4IgI/Tja7MbVf-uI/AAAAAAAAAYQ/nde6VIRt3aY/s220/august_and_me.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4314202365991499343.post-2595282263976317890</id><published>2010-02-03T23:34:00.001-08:00</published><updated>2010-02-03T23:34:15.856-08:00</updated><title type='text'>Google Latitude finally locates me</title><content type='html'>&lt;p&gt;I joined Google Latitude more than half year ago but no matter whether I’m at home (Rancho Cucamonga, CA) or at my office (Redlands, CA), it always tells me that I’m in Hague Netherlands, which I assume is what my friends will see too. Recently it finally corrects the location to Rancho Cucamonga or Redlands. Since I am not using Google Latitude on my mobile device, the only way Google Latitude can rely on should be the IP address and associated ISP. Just wonder why there could be such a big mistake.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4314202365991499343-2595282263976317890?l=augusttown.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://augusttown.blogspot.com/feeds/2595282263976317890/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://augusttown.blogspot.com/2010/02/google-latitude-finally-locates-me.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/2595282263976317890'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/2595282263976317890'/><link rel='alternate' type='text/html' href='http://augusttown.blogspot.com/2010/02/google-latitude-finally-locates-me.html' title='Google Latitude finally locates me'/><author><name>Yingqi Tang</name><uri>http://www.blogger.com/profile/00422288991527433117</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/--JG8BCe4IgI/Tja7MbVf-uI/AAAAAAAAAYQ/nde6VIRt3aY/s220/august_and_me.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4314202365991499343.post-6731641508204271958</id><published>2010-01-22T16:06:00.001-08:00</published><updated>2010-01-22T16:06:11.769-08:00</updated><title type='text'>First experience of armchair mapping using Mapzen and JSOM WMS plug-in</title><content type='html'>&lt;p&gt;In &lt;a href="http://augusttown.blogspot.com/2010/01/very-first-experience-of-uploading-osm_07.html"&gt;previous post&lt;/a&gt; I shared my first experience of making changes to OSM database with Potlatch and JOSM. As I mentioned there both of them are good tools but neither is perfect so I’m still looking for some alternatives. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://mapzen.cloudmade.com/map"&gt;Mapzen&lt;/a&gt; caught my eye recently probably because it becomes quite a popular buzz word in blogs and news feedings so I decided to give it a try. At first glance, Mapzen looks quite similar to Potlatch (&lt;a href="http://cloudmade.com/"&gt;CloudMade&lt;/a&gt; who hosts Mapzen is founded by the same person who started OSM): &lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;1. It requires an account (you can register it for free) to work with;&lt;/p&gt;    &lt;p&gt;2. A peek at source of Mapzen front page shows it’s based on Flex too;&lt;/p&gt;    &lt;p&gt;3. It uses OSM map as base layer in view mode;&lt;/p&gt;    &lt;p&gt;4. It uses Yahoo Imagery as based layer in edit mode; (by the way, Yahoo Aerial Imagery is probably the only imagery available for free which has decent globe coverage. I never see other options, do you?)&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Mapzen in view mode:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_qgvb6YVf5Fw/S1o9V6jAVjI/AAAAAAAAAHs/zDTh0mQl18Q/s1600-h/14.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="1" border="0" alt="1" src="http://lh4.ggpht.com/_qgvb6YVf5Fw/S1o9WcNIpnI/AAAAAAAAAHw/h8UIpCoqpj4/1_thumb2.png?imgmax=800" width="610" height="484" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Mapzen in edit mode:&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_qgvb6YVf5Fw/S1o9W81E50I/AAAAAAAAAH0/4QflJd2YCtI/s1600-h/23.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="2" border="0" alt="2" src="http://lh6.ggpht.com/_qgvb6YVf5Fw/S1o9ZLaHu4I/AAAAAAAAAH4/9yLOtR62S-s/2_thumb1.png?imgmax=800" width="644" height="422" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;I like the way it categorize those pre-defined features in Points, Lines and Shapes and each one of the feature types has a meaningful icon associated. Of course it lacks some of the advanced editing functionalities that are available in Potlatch (it seems to have a tool to line up points) and JOSM, but it has more than enough for majority of the users. Another thing I like Mapzen over Potlatch is the look and feel when you draw the geometry on map. With Potlatch I always feel like using a big brush for wall painting, where all I actually need is pencil for sketch. The only weakness though is that I didn’t find a way to upload data as input for my editing, either through an osm file or a gpx track while Potlatch provides that option. Everything must be hand drawn. But anyway you can always combine the power of both.&lt;/p&gt;  &lt;p&gt;After editing simply click “Save Map” button at corner (you might want to input some notes for this particular editing). The picture above shows the hand-draw building and foot way of my house using Mapzen, as well as a BBQ island garden “August Town” (named after my boy).&lt;/p&gt;  &lt;p&gt;&lt;a href="http://josm.openstreetmap.de/"&gt;JOSM&lt;/a&gt; with its &lt;a href="http://josm.openstreetmap.de/wiki/Plugins"&gt;WMS plugin&lt;/a&gt; (with the Yahoo Aerial Imagery extension) is another option I tried after Mapzen, which I probably rank the highest among all three of them (Potlatch, Mapzen and JOSM). Like I mentioned before, the lack of a decent base imagery is a huge bottleneck which makes it almost impossible for armchair mapping, but the WMS plug-in with Yahoo Aerial Imagery extension overcomes it like magic. So let’s equip with it first by following the instructions &lt;a href="http://wiki.openstreetmap.org/wiki/JOSM/Plugins/WMSPlugin"&gt;here&lt;/a&gt;. A brief list of steps:&lt;/p&gt;  &lt;p&gt;Install WMS plugin itself:&lt;/p&gt;  &lt;blockquote&gt;   &lt;h5&gt;With the plugin manager &lt;/h5&gt;    &lt;p&gt;You can easily install plugins from within JOSM as follows &lt;/p&gt;    &lt;ol&gt;     &lt;li&gt;Start JOSM, open the preferences window (Edit-&amp;gt;Preferences or use the toolbar icon) and select the plugins tab. &lt;/li&gt;      &lt;li&gt;Click on &amp;quot;Download List&amp;quot; to download the list of available plugins. &lt;/li&gt;      &lt;li&gt;Check the plugins you want installed. &lt;/li&gt;      &lt;li&gt;Click the accept button. All new plugins should start downloading and installing. &lt;/li&gt;      &lt;li&gt;Restart JOSM. &lt;/li&gt;   &lt;/ol&gt;   &lt;a name="Manually"&gt;&lt;/a&gt;    &lt;h5&gt;Manually &lt;/h5&gt;    &lt;p&gt;With older versions (up until &lt;a href="http://josm.openstreetmap.de/changeset/277"&gt;277&lt;/a&gt;), you have to install the plugins manually. &lt;/p&gt;    &lt;ol&gt;     &lt;li&gt;Download the plugin file from wherever the plugin is hosted. Look in the plugin page or the &lt;a href="http://josm.openstreetmap.de/wiki/Plugins"&gt;'Plugins'&lt;/a&gt; page on the JOSM wiki site for the location of this file. &lt;/li&gt;      &lt;li&gt;The file should have an &amp;quot;.jar&amp;quot; extension. If it doesn't, rename the downloaded file so that it ends with &amp;quot;.jar&amp;quot;. Internet Explorer, for instance, may rename some files to &amp;quot;.zip&amp;quot;. &lt;/li&gt;      &lt;li&gt;Move the .jar file to the JOSM preferences directory (&amp;quot;%APPDATA%/JOSM&amp;quot; in windows, &amp;quot;~/.josm/plugins/&amp;quot; in Unix/MacOS.) &lt;/li&gt;      &lt;li&gt;Start JOSM, open the preferences window (Edit-&amp;gt;Preferences) and select the plugins tab. &lt;/li&gt;      &lt;li&gt;Activate the plugin in the plugins tab. &lt;/li&gt;      &lt;li&gt;Restart JOSM. &lt;/li&gt;   &lt;/ol&gt; &lt;/blockquote&gt;  &lt;p&gt;Install Yahoo! Aerial Imagery Downloader&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;On &lt;b&gt;Windows&lt;/b&gt; use the &lt;a href="http://de.wikipedia.org/wiki/WebKit"&gt;WebKit&lt;/a&gt; based downloader called webkit-image as follows &lt;/p&gt;    &lt;ul&gt;     &lt;li&gt;&lt;a href="http://josm.openstreetmap.de/download/windows/webkit-image.zip"&gt;Download webkit-image.zip&lt;/a&gt; &lt;/li&gt;      &lt;li&gt;Unzip it. &lt;/li&gt;      &lt;li&gt;Move the contents so that the DLL files and the EXE file are somewhere &amp;quot;on your system path&amp;quot; (eg. c:\windows ). The best way to achieve this might be to place them alongside josm-latest.jar Keep the 'imageformats' subfolder alongside too (so &lt;i&gt;all&lt;/i&gt; the contents of the zip). &lt;/li&gt;      &lt;li&gt;Restart JOSM &lt;/li&gt;      &lt;li&gt;Do 'WMS' menu -&amp;gt; YAHOO (Webkit) &lt;/li&gt;   &lt;/ul&gt;    &lt;p&gt;You should start to get Yahoo! imagery (may take up to 30 seconds to start showing). If not, it may not be finding the DLL files correctly. &lt;/p&gt;    &lt;p&gt;&lt;b&gt;Note:&lt;/b&gt; If you don't want to place webkit-image in your system path or the JOSM directory you don't have to. By editing the download program you can specify an absolute or relative path to the webkit-image executable. Examples: &lt;/p&gt;    &lt;ul&gt;     &lt;li&gt;&lt;b&gt;webkit/webkit-image {0}&lt;/b&gt; - loads webkit-image.exe from the subdirectory &lt;b&gt;webkit&lt;/b&gt; relative to the JOSM installation directory &lt;/li&gt;      &lt;li&gt;&lt;b&gt;D:/webkit/webkit-image {0}&lt;/b&gt; - uses webkit-image &lt;b&gt;D:\webkit\webkit-image.exe&lt;/b&gt; &lt;/li&gt;   &lt;/ul&gt; &lt;/blockquote&gt;  &lt;p&gt;After a successful install, restart JOSM and you should a new menu called “WMS” like below:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_qgvb6YVf5Fw/S1o9ZpqkzPI/AAAAAAAAAH8/Y7A5tBZ8FZM/s1600-h/33.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="3" border="0" alt="3" src="http://lh6.ggpht.com/_qgvb6YVf5Fw/S1o9Z73kOhI/AAAAAAAAAIA/DdDOGtwF0KY/3_thumb1.png?imgmax=800" width="595" height="484" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Now download your data from OSM database as usual first, and after select the “WMS” menu and select “Yahoo Sat”. Wait for about 5, 6 seconds you will see a nice aerial imagery base layer underneath your OSM data.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_qgvb6YVf5Fw/S1o9aZrZHCI/AAAAAAAAAIE/gODGbLZx4a4/s1600-h/43.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="4" border="0" alt="4" src="http://lh6.ggpht.com/_qgvb6YVf5Fw/S1o9bIaDloI/AAAAAAAAAIM/_uaFs7oKiVU/4_thumb1.png?imgmax=800" width="617" height="484" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Well done! And this is perfect for armchair mapping. Now enjoy your smooth mapping experience with powerful tools in JOSM. I didn’t try out the WMS layer and rectified image yet which can definitely be better base layer options for specific area.&lt;/p&gt;  &lt;p&gt;After the editing simply click upload button (right next to download button) and upload the changes. (you may need to set your OSM account username and password)&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_qgvb6YVf5Fw/S1o9boi-bgI/AAAAAAAAAIQ/2sAWRGt2B34/s1600-h/6%5B3%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="6" border="0" alt="6" src="http://lh5.ggpht.com/_qgvb6YVf5Fw/S1o9cNKOs1I/AAAAAAAAAIU/NinrSKsAJms/6_thumb%5B1%5D.png?imgmax=800" width="618" height="484" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_qgvb6YVf5Fw/S1o9cbSYnhI/AAAAAAAAAIY/JtZ_r-niW78/s1600-h/5%5B6%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="5" border="0" alt="5" src="http://lh3.ggpht.com/_qgvb6YVf5Fw/S1o9c0GFFMI/AAAAAAAAAIc/C8W9F8maTbY/5_thumb%5B2%5D.png?imgmax=800" width="598" height="597" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;So my conclusion is that JOSM with WMS plugin is an obvious winner due to the smooth and rich desktop UI experience if you’re doing armchair mapping at home. But Potlatch and Mapzen have their own advantages of being browser based and easy to use for non-professional users.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4314202365991499343-6731641508204271958?l=augusttown.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://augusttown.blogspot.com/feeds/6731641508204271958/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://augusttown.blogspot.com/2010/01/first-experience-of-armchair-mapping.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/6731641508204271958'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/6731641508204271958'/><link rel='alternate' type='text/html' href='http://augusttown.blogspot.com/2010/01/first-experience-of-armchair-mapping.html' title='First experience of armchair mapping using Mapzen and JSOM WMS plug-in'/><author><name>Yingqi Tang</name><uri>http://www.blogger.com/profile/00422288991527433117</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/--JG8BCe4IgI/Tja7MbVf-uI/AAAAAAAAAYQ/nde6VIRt3aY/s220/august_and_me.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_qgvb6YVf5Fw/S1o9WcNIpnI/AAAAAAAAAHw/h8UIpCoqpj4/s72-c/1_thumb2.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4314202365991499343.post-4892382444988103225</id><published>2010-01-20T09:45:00.001-08:00</published><updated>2010-01-20T09:47:34.420-08:00</updated><title type='text'>Install Apache HTTP Server + PHP5 on Windows 7 (64bit)</title><content type='html'>Recent installation of &lt;a href="http://www.dokuwiki.org/dokuwiki"&gt;dokuwiki&lt;/a&gt; (a simple web-based wiki engine) requires a combination of Apache HTTP Server + PHP5 on my 64-bit Windows 7 machine, but setup process didn’t seem to be as simple as “following the wizards”. So I decide to jot a few lines down such that I don’t have to search on the web again when I need to repeat the installation.&lt;br /&gt;&lt;br /&gt;1. Install Apache HTTP Server; (just go to the &lt;a href="http://httpd.apache.org/download.cgi"&gt;official site&lt;/a&gt; and pick up .msi, and install in default path C:\Program Files(x86)\Apache Software Foundation\ seems to be ok)&lt;br /&gt;2. Install PHP5;&lt;br /&gt;&lt;br /&gt;This worth a short paragraph. My understanding of the &lt;a href="http://www.php.net/manual/en/install.windows.installer.msi.php"&gt;install steps on PHP website&lt;/a&gt; is that it should be automatically taken care of by the installation wizards if it is for a popular http server like apache, and the install wizard UI does indicate that too. But unfortunately I didn’t get it to work in which I either got an general error message or no error message but just not working. So I switch to the &lt;a href="http://windows.php.net/downloads/releases/php-5.3.1-nts-Win32-VC9-x86.zip"&gt;zip version&lt;/a&gt; of PHP5 which is suggested by some article online and following the steps below makes it work properly:&lt;br /&gt;&lt;blockquote&gt;2.1 Unzip the the zip version of PHP5 to your local disk; (unzip it to C:\Program Files(x86)\PHP5 seems to be working too)&lt;br /&gt;2.2 Open httpd.conf file of Apache HTTP server in a text editor;&lt;br /&gt;2.3 At the end of the http.conf file add following lines to make Apache HTTP Server be aware of PHP5; (adjust you path accordingly)&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: # Begin enable php5&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2: LoadModule php5_module "C:/Program Files (x86)/PHP5/php5apache2_2.dll"&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3: AddType application/x-httpd-php .php&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4: PHPIniDir "C:/Program Files (x86)/PHP5"&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5: # End enable php5&lt;/pre&gt;&lt;/pre&gt;&lt;/blockquote&gt;&lt;blockquote&gt;2.4 Save the change and restart Apache HTTP Server, and if successful you should see &lt;br /&gt;“Apache/2.2.14(Win32) PHP/5.3.1” in the status bar of Apache Service Monitor.&lt;br /&gt;&lt;/blockquote&gt;Now your Apache HTTP Server should be able to display php content, at least my php based dokuwiki is running perfect.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4314202365991499343-4892382444988103225?l=augusttown.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://augusttown.blogspot.com/feeds/4892382444988103225/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://augusttown.blogspot.com/2010/01/install-apache-http-server-php5-on.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/4892382444988103225'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/4892382444988103225'/><link rel='alternate' type='text/html' href='http://augusttown.blogspot.com/2010/01/install-apache-http-server-php5-on.html' title='Install Apache HTTP Server + PHP5 on Windows 7 (64bit)'/><author><name>Yingqi Tang</name><uri>http://www.blogger.com/profile/00422288991527433117</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/--JG8BCe4IgI/Tja7MbVf-uI/AAAAAAAAAYQ/nde6VIRt3aY/s220/august_and_me.jpeg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4314202365991499343.post-6258957524467053947</id><published>2010-01-19T22:57:00.001-08:00</published><updated>2010-01-19T22:57:27.494-08:00</updated><title type='text'>Google Maps Navigation failed me again</title><content type='html'>&lt;p&gt;As an Android G1 mobile phone user and a fan of Google products and services, I was really excited when the GPS killer app Google Maps Navigation was ported to Android 1.6. But I don’t usually drive to a lot of new places that I have no idea how to get to, even when I do I will do my homework on desktop first, which leaves Google Maps Navigation on my Android phone very few chances to prove its value. But the only time I was trying to rely on it to help me out, it failed. That was about one month ago, I am on my way to metro station in Riverside to pick up my wife after work. Due to the heavy traffic on CA-91 I decided to switch to the station right after the original one (Riverside-La Sierra station), which as I recall later was the first time Google Maps Navigation on my G1 phone coming to rescue in a real situation rather than a fake drill. It actually did a very good job for the most part, which I even see the metro station I was looking for (the red place marker). But amazing thing happened when I passed by the destination (I should have followed the green dash line). First Google Maps Navigation didn’t tell me that I had arrived (I had a second thought that I should just turn into the parking lot, but I hesitated because Google never failed me before), instead it told me to get onto the highway that I just got off again in other direction (the solid green line). I followed although with a little doubt (still believe Google was right) but was totally pissed off after a few seconds when right in the middle of the ramp (where the red cross is) it said that I’ve arrived.&lt;/p&gt;  &lt;p&gt;I followed the solid green line which Google Maps Navigation told me to, but the red cross is the place where it said that I’ve arrived.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_qgvb6YVf5Fw/S1apEdpaLvI/AAAAAAAAAHM/cgS7QMWwibs/s1600-h/4%5B3%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="4" border="0" alt="4" src="http://lh6.ggpht.com/_qgvb6YVf5Fw/S1apE3ygubI/AAAAAAAAAHQ/5OlzpdBqcEA/4_thumb%5B1%5D.png?imgmax=800" width="465" height="453" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Today the fancy free Google Maps Navigation failed me again. I was looking for Bed Bath &amp;amp; Beyond in Mira Loma, CA, which seems to be pretty new because my 3-year old Magellan GPS wasn’t able to find it at all. Searching in Google Maps Navigation (by typing “Bed Bath and Beyond” and click search) gave me a matching Bed Bath &amp;amp; Beyond in Mira Loma, which has the accurate telephone number and address. But obviously it put the spin at a wrong place which is about half mile north west of the actual location in a lake. Using the same searching pattern in Google Maps generates the same error, but if you type the result address back in both Google Maps and Navigation points to the right place.&lt;/p&gt;  &lt;p&gt;Bed Bath &amp;amp; Beyond in Swan Lake&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_qgvb6YVf5Fw/S1apFQkvUtI/AAAAAAAAAHU/R9e9ulRFmM0/s1600-h/2%5B12%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="2" border="0" alt="2" src="http://lh4.ggpht.com/_qgvb6YVf5Fw/S1apGBQt75I/AAAAAAAAAHY/FLruO8wwieY/2_thumb%5B8%5D.png?imgmax=800" width="548" height="513" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;The red place marker is where Google Maps Navigation directed me to, while the green spin is the correct place:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_qgvb6YVf5Fw/S1apGaUUS6I/AAAAAAAAAHc/yVGptJF_GDQ/s1600-h/1%5B3%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="1" border="0" alt="1" src="http://lh5.ggpht.com/_qgvb6YVf5Fw/S1apHDPOJpI/AAAAAAAAAHg/RWykEDY0ZDY/1_thumb%5B1%5D.png?imgmax=800" width="476" height="474" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;Another thing I notice is that the streets around wrong Bed Bath &amp;amp; Beyond location in Google Maps hasn’t been updated at all, or at least not recent enough to reflect current name. An example is that the “Peachtree Dr” in red circle doesn’t exist any more. Looking at OpenStreetMap for the same area gives me the accurate result as below:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_qgvb6YVf5Fw/S1apVLZWItI/AAAAAAAAAHk/phaHcAEMSjw/s1600-h/3%5B3%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="3" border="0" alt="3" src="http://lh5.ggpht.com/_qgvb6YVf5Fw/S1apVsuIvLI/AAAAAAAAAHo/4IrB8HjBMEQ/3_thumb%5B1%5D.png?imgmax=800" width="555" height="514" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4314202365991499343-6258957524467053947?l=augusttown.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://augusttown.blogspot.com/feeds/6258957524467053947/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://augusttown.blogspot.com/2010/01/google-maps-navigation-failed-me-again.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/6258957524467053947'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/6258957524467053947'/><link rel='alternate' type='text/html' href='http://augusttown.blogspot.com/2010/01/google-maps-navigation-failed-me-again.html' title='Google Maps Navigation failed me again'/><author><name>Yingqi Tang</name><uri>http://www.blogger.com/profile/00422288991527433117</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/--JG8BCe4IgI/Tja7MbVf-uI/AAAAAAAAAYQ/nde6VIRt3aY/s220/august_and_me.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_qgvb6YVf5Fw/S1apE3ygubI/AAAAAAAAAHQ/5OlzpdBqcEA/s72-c/4_thumb%5B1%5D.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4314202365991499343.post-480627471363581629</id><published>2010-01-15T14:47:00.001-08:00</published><updated>2010-01-15T14:56:48.359-08:00</updated><title type='text'>Customize WMS GetFeatureInfo response :: GeoServer 2.0 versus MapServer 5.4.2</title><content type='html'>OGC Web Map Service (WMS) specification is a simple and popular protocol for serving maps over the web nowadays. But everything has two sides and the weakness of WMS is that it doesn’t define a standard format for its GetFeatureInfo operation, neither does it reference any existing standard like GML or so. The direct consequence of that is the truth that most of today’s WMS server implementations speaks different “languages” in their query results which results in the opposite of interoperability between servers and clients from different vendors.  &lt;br /&gt;&lt;br /&gt;Using HTML for WMS GetFeatureInfo somehow alleviate the situation, because without parsing clients can just throw HTML to browser. Some other implementations support GML to be more interoperable, but again those are just workarounds and the real issue should be solved in spec itself.   &lt;br /&gt;Anyway while waiting for the next version of spec (1.4.0?, 2.0? or else) recently, I spent some time with GeoServer 2.0 and MapServer 5.4.2 to customize the WMS GetFeatureInfo response in HTML, which is a feature available in both popular open source WMS server implementations.   &lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;What is the goal?&lt;/b&gt;&lt;/u&gt;  &lt;br /&gt;&lt;br /&gt;I have a simple vector dataset for San Francisco area in shape file format, which I published as WMS in both GeoServer and MapServer. Below is the map I will get when I send a WMS GetMap request:   &lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;span style="color: #0080ff;"&gt;&lt;a href="http://%3cwms_service_url%3e/?REQUEST=GetMap&amp;amp;SERVICE=WMS&amp;amp;VERSION=1.1.1&amp;amp;LAYERS=blockgroups,highways,pizzastores&amp;amp;STYLES=&amp;amp;FORMAT=image/png&amp;amp;BGCOLOR=0xEEEEEE&amp;amp;TRANSPARENT=false&amp;amp;SRS=EPSG:4326&amp;amp;BBOX=-122.545074509804,37.6736653056517,-122.35457254902,37.8428758708189&amp;amp;WIDTH=1020&amp;amp;HEIGHT=906"&gt;http://&amp;lt;wms_service_url&amp;gt;?REQUEST=GetMap&amp;amp;SERVICE=WMS&amp;amp;VERSION=1.1.1&amp;amp;LAYERS=blockgroups,highways,pizzastores&amp;amp;STYLES=&amp;amp;FORMAT=image/png&amp;amp;BGCOLOR=0xEEEEEE&amp;amp;TRANSPARENT=false&amp;amp;SRS=EPSG:4326&amp;amp;BBOX=-122.545074509804,37.6736653056517,-122.35457254902,37.8428758708189&amp;amp;WIDTH=1020&amp;amp;HEIGHT=906&lt;/a&gt;&lt;/span&gt;&lt;/u&gt;  &lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: left;"&gt;&lt;a href="http://lh5.ggpht.com/_qgvb6YVf5Fw/S1DwkZ8PvFI/AAAAAAAAAGs/aJflt3kSosI/s1600-h/sf7.png"&gt;&lt;img alt="sf" border="0" height="484" src="http://lh3.ggpht.com/_qgvb6YVf5Fw/S1Dwk_-vYGI/AAAAAAAAAGw/77H7D3d5jcA/sf_thumb3.png?imgmax=800" style="border-width: 0px; display: block; float: none; margin-left: auto; margin-right: auto;" title="sf" width="544" /&gt;&lt;/a&gt;    &lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;Now if I send a GetFeatureInfo request to query features on the map:   &lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;u&gt;&lt;span style="color: #0080ff;"&gt;&lt;a href="http://%3cwms_service_url%3e/"&gt;http://&amp;lt;wms_service_url&amp;gt;&lt;/a&gt;&lt;/span&gt;&lt;/u&gt;?&lt;a href="http://&amp;amp;request=getfeatureinfo&amp;amp;service=wms&amp;amp;version=1.1.1&amp;amp;layers=pizzastores,highways,blockgroups&amp;amp;styles=&amp;amp;format=image/png&amp;amp;BGCOLOR=0xFFFFFF&amp;amp;TRANSPARENT=TRUE&amp;amp;SRS=EPSG:4326&amp;amp;BBOX=-122.545074509804,37.6736653056517,-122.35457254902,37.8428758708189&amp;amp;WIDTH=1020&amp;amp;HEIGHT=906&amp;amp;QUERY_LAYERS=pizzastores,highways,blockgroups&amp;amp;X=652&amp;amp;Y=368&amp;amp;INFO_FORMAT=text/html"&gt;REQUEST=GetFeatureInfo&amp;amp;SERVICE=WMS&amp;amp;VERSION=1.1.1&amp;amp;LAYERS=pizzastores,highways,blockgroups&amp;amp;STYLES=&amp;amp;FORMAT=image/png&amp;amp;BGCOLOR=0xFFFFFF&amp;amp;TRANSPARENT=TRUE&amp;amp;SRS=EPSG:4326&amp;amp;BBOX=-122.545074509804,37.6736653056517,-122.35457254902,37.8428758708189&amp;amp;WIDTH=1020&amp;amp;HEIGHT=906&amp;amp;QUERY_LAYERS=pizzastores,highways,blockgroups&amp;amp;X=652&amp;amp;Y=368&amp;amp;INFO_FORMAT=text/html&lt;/a&gt;   &lt;br /&gt;&lt;br /&gt;Zero or more features records will be returned (Note: this particular request actually returns records from all three layers: ‘pizzastores’, ‘highways’, and ‘blockgroups’). So what I am trying to achieve here is to have both GeoServer and MapServer output GetFeatureInfo results in html with styles like below:  &lt;br /&gt;&lt;div style="text-align: left;"&gt;&lt;a href="http://lh6.ggpht.com/_qgvb6YVf5Fw/S1DwlOeNY7I/AAAAAAAAAG0/axZ5rCczzH0/s1600-h/sf23.png"&gt;&lt;img alt="sf2" border="0" height="143" src="http://lh5.ggpht.com/_qgvb6YVf5Fw/S1DwlhxGS6I/AAAAAAAAAG4/RYxHtVbpnlk/sf2_thumb1.png?imgmax=800" style="border-width: 0px; display: block; float: none; margin-left: auto; margin-right: auto;" title="sf2" width="644" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;It’s a very simple customization but it proves the ability in GeoServer and MapServer, and of course more rich HTML elements like charts &amp;amp; pies, audios and videos can be easily added.  &lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;u&gt;MapServer 5.4.2&lt;/u&gt;&lt;/b&gt;  &lt;br /&gt;For MapServer (the ms4w.exe that contains MapServer 5.4.2) I finally made what I planned, but I have to say that the whole user experience is far less smooth than I expected. A non-guru user like me mostly replies on the doc and samples as a start point, but the information regarding to this topic is scattered all over piece by piece without links pointing to each other. So compared to this I liked the new GeoServer 2.0 user manual much better.  &lt;br /&gt;&lt;br /&gt;To make shape file layer queryable in MapServer WMS, I started from a sample map file that I modified from online sample, and here is a section for one of my layers  &lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: &lt;span style="color: maroon;"&gt;#&lt;/span&gt; ===============================================================================&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2: &lt;span style="color: maroon;"&gt;#&lt;/span&gt; &lt;span style="color: maroon;"&gt;highways&lt;/span&gt; &lt;span style="color: maroon;"&gt;layer&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3: &lt;span style="color: maroon;"&gt;#&lt;/span&gt; ===============================================================================&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4: &lt;span style="color: maroon;"&gt;LAYER&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:     &lt;span style="color: maroon;"&gt;NAME&lt;/span&gt; "&lt;span style="color: maroon;"&gt;highways&lt;/span&gt;"    &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6:     &lt;span style="color: maroon;"&gt;METADATA&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7:         "&lt;span style="color: maroon;"&gt;ows&lt;/span&gt;_&lt;span style="color: maroon;"&gt;title&lt;/span&gt;" "&lt;span style="color: maroon;"&gt;highways&lt;/span&gt;"&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8:         "&lt;span style="color: maroon;"&gt;ows&lt;/span&gt;_&lt;span style="color: maroon;"&gt;srs&lt;/span&gt;"   "&lt;span style="color: maroon;"&gt;EPSG&lt;/span&gt;:&lt;span style="color: maroon;"&gt;4326&lt;/span&gt;"&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9:     &lt;span style="color: maroon;"&gt;END&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;10:     &lt;span style="color: maroon;"&gt;TYPE&lt;/span&gt; &lt;span style="color: maroon;"&gt;LINE&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;11:     &lt;span style="color: maroon;"&gt;STATUS&lt;/span&gt; &lt;span style="color: maroon;"&gt;ON&lt;/span&gt;    &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;12:     &lt;span style="color: maroon;"&gt;DATA&lt;/span&gt; "./&lt;span style="color: maroon;"&gt;sanfrancisco&lt;/span&gt;/&lt;span style="color: maroon;"&gt;shp&lt;/span&gt;/&lt;span style="color: maroon;"&gt;highways&lt;/span&gt;"    &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;13:     &lt;span style="color: maroon;"&gt;PROJECTION&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;14:         "&lt;span style="color: maroon;"&gt;init&lt;/span&gt;=&lt;span style="color: maroon;"&gt;epsg&lt;/span&gt;:&lt;span style="color: maroon;"&gt;4326&lt;/span&gt;"&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;15:     &lt;span style="color: maroon;"&gt;END&lt;/span&gt;    &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;16:     &lt;span style="color: maroon;"&gt;CLASS&lt;/span&gt;    &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;17:         &lt;span style="color: maroon;"&gt;NAME&lt;/span&gt; "&lt;span style="color: maroon;"&gt;highways&lt;/span&gt;"            &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;18:         &lt;span style="color: maroon;"&gt;STYLE&lt;/span&gt;        &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;19:             &lt;span style="color: maroon;"&gt;WIDTH&lt;/span&gt; &lt;span style="color: maroon;"&gt;1&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;20:             &lt;span style="color: maroon;"&gt;COLOR&lt;/span&gt; &lt;span style="color: maroon;"&gt;0&lt;/span&gt; &lt;span style="color: maroon;"&gt;0&lt;/span&gt; &lt;span style="color: maroon;"&gt;255&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;21:         &lt;span style="color: maroon;"&gt;END&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;22:     &lt;span style="color: maroon;"&gt;END&lt;/span&gt;      &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;23: &lt;span style="color: maroon;"&gt;END&lt;/span&gt;&lt;/pre&gt;&lt;/pre&gt;But unfortunately, all WMS layers are defined unqueryable (you will see &amp;lt;Layer …queryable=”0”…&amp;gt; in WMS capabilities files) by default, and online documentation doesn’t say anything clear on how to enable query on WMS layers. I figured out in the end by searching through the &lt;a href="http://n2.nabble.com/Mapserver-User-f1969211.html"&gt;forum&lt;/a&gt;, in which some others people are asking the similar questions. Basically you have to add following line in each layer definition in map file:&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: &lt;span style="color: maroon;"&gt;LAYER&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2:     ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: yellow; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:     &lt;span style="color: maroon;"&gt;TEMPLATE&lt;/span&gt; "&lt;span style="color: maroon;"&gt;blank&lt;/span&gt;.&lt;span style="color: maroon;"&gt;html&lt;/span&gt;"&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:     ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5: &lt;span style="color: maroon;"&gt;END&lt;/span&gt;&lt;/pre&gt;&lt;/pre&gt;“TEMPLATE” is suppose to point to a html file used as a template for WMS query result, and it makes WMS layer queryable even though the html file you’re pointing to doesn’t exists (I just feel a little awkward about this)&lt;br /&gt;&lt;br /&gt;Now I can actually get GetFeatureInfo response from WMS, but I encountered three more problems right way:&lt;br /&gt;&lt;br /&gt;1. GetFeatureInfo response doesn’t support GML as it claims; by reading the &lt;a href="http://mapserver.org/ogc/wms_server.html#reference-section"&gt;MapServer WMS doc “Reference Section”&lt;/a&gt; it can be solved by adding “DUMP TRUE” into layer definition:&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: &lt;span style="color: maroon;"&gt;LAYER&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2:     ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: yellow; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:     &lt;span style="color: maroon;"&gt;DUMP&lt;/span&gt; &lt;span style="color: maroon;"&gt;TRUE&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:     ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5: &lt;span style="color: maroon;"&gt;END&lt;/span&gt;&lt;/pre&gt;&lt;/pre&gt;2. Either GML response or plain text response of GetFeatureInfo doesn’t include any attribute of the result feature; by reading the doc, you can solve that for GML by adding “&lt;b&gt;gml_include_items&amp;nbsp;&amp;nbsp;&amp;nbsp; all&lt;/b&gt;” in metadata of layer definition. But I didn’t complete get rid of the problem until I searched through the forum again and found another undocumented “wms_include_items”. So what you need is:&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: &lt;span style="color: maroon;"&gt;LAYER&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2:     ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:     &lt;span style="color: maroon;"&gt;METADATA&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:         ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: yellow; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:         "&lt;span style="color: maroon;"&gt;gml&lt;/span&gt;_&lt;span style="color: maroon;"&gt;include&lt;/span&gt;_&lt;span style="color: maroon;"&gt;items&lt;/span&gt;"   "&lt;span style="color: maroon;"&gt;all&lt;/span&gt;"&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: yellow; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6:         "&lt;span style="color: maroon;"&gt;wms&lt;/span&gt;_&lt;span style="color: maroon;"&gt;include&lt;/span&gt;_&lt;span style="color: maroon;"&gt;items&lt;/span&gt;"   "&lt;span style="color: maroon;"&gt;all&lt;/span&gt;"&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7:         ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8:     &lt;span style="color: maroon;"&gt;END&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9:     ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;10: &lt;span style="color: maroon;"&gt;END&lt;/span&gt;&lt;/pre&gt;&lt;/pre&gt;3. “text/html” is not supported in GetFeatureInfo response; this is also documented but in a very obvious place. “&lt;b&gt;wms_feature_info_mime_type&amp;nbsp;&amp;nbsp;&amp;nbsp; text/html&lt;/b&gt;” in the web section of the map files fix the problem:&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: &lt;span style="color: maroon;"&gt;Map&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2:     ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:     &lt;span style="color: maroon;"&gt;WEB&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:         ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: yellow; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:         "&lt;span style="color: maroon;"&gt;wms&lt;/span&gt;_&lt;span style="color: maroon;"&gt;feature&lt;/span&gt;_&lt;span style="color: maroon;"&gt;info&lt;/span&gt;_&lt;span style="color: maroon;"&gt;mime&lt;/span&gt;_&lt;span style="color: maroon;"&gt;type&lt;/span&gt;" "&lt;span style="color: maroon;"&gt;text&lt;/span&gt;/&lt;span style="color: maroon;"&gt;html&lt;/span&gt;"&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6:         ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7:     &lt;span style="color: maroon;"&gt;END&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8:     ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9: &lt;span style="color: maroon;"&gt;END&lt;/span&gt;&lt;/pre&gt;&lt;/pre&gt;Until now I can finally start creating the html template for GetFeatureInfo response. Although not specific to WMS GetFeatureInfo response, there is a detailed documentation page on MapServer template. As expected, you can define a header template, a footer template and another template for the content. &lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: &lt;span style="color: maroon;"&gt;LAYER&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2:     ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:     &lt;span style="color: maroon;"&gt;HEADER&lt;/span&gt; "../&lt;span style="color: maroon;"&gt;template&lt;/span&gt;/&lt;span style="color: maroon;"&gt;getfeatureinfo&lt;/span&gt;_&lt;span style="color: maroon;"&gt;header&lt;/span&gt;.&lt;span style="color: maroon;"&gt;html&lt;/span&gt;" &lt;span style="color: maroon;"&gt;#&lt;/span&gt; &lt;span style="color: maroon;"&gt;header&lt;/span&gt; &lt;span style="color: maroon;"&gt;html&lt;/span&gt; &lt;span style="color: maroon;"&gt;template&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:     &lt;span style="color: maroon;"&gt;TEMPLATE&lt;/span&gt; "../&lt;span style="color: maroon;"&gt;template&lt;/span&gt;/&lt;span style="color: maroon;"&gt;getfeatureinfo&lt;/span&gt;_&lt;span style="color: maroon;"&gt;content&lt;/span&gt;.&lt;span style="color: maroon;"&gt;html&lt;/span&gt;" &lt;span style="color: maroon;"&gt;#&lt;/span&gt; &lt;span style="color: maroon;"&gt;content&lt;/span&gt; &lt;span style="color: maroon;"&gt;html&lt;/span&gt; &lt;span style="color: maroon;"&gt;template&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:     &lt;span style="color: maroon;"&gt;FOOTER&lt;/span&gt; "../&lt;span style="color: maroon;"&gt;template&lt;/span&gt;/&lt;span style="color: maroon;"&gt;getfeatureinfo&lt;/span&gt;_&lt;span style="color: maroon;"&gt;footer&lt;/span&gt;.&lt;span style="color: maroon;"&gt;html&lt;/span&gt;" &lt;span style="color: maroon;"&gt;#&lt;/span&gt; &lt;span style="color: maroon;"&gt;footer&lt;/span&gt; &lt;span style="color: maroon;"&gt;html&lt;/span&gt; &lt;span style="color: maroon;"&gt;template&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6:     ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7: &lt;span style="color: maroon;"&gt;END&lt;/span&gt;&lt;/pre&gt;&lt;/pre&gt;All three html templates are specified at layer level which allows you to customize GetFeatureInfo response differently for individual layer. Two pros of MapServer template are (1) a lot of server related information are exposed in template which you can reference by operation “[]”, e.g. server host, port, map and layer metadata etc instead of just limited to query results of GetFeatureInfo;&amp;nbsp; (2) since there is no other template language involved, I always feel easier to embed javascript code in template which is a big plus. But there are also many cons in this work flow too. If you have more than one included in WMS “query_layers” and the query result has multiple features then the the html content in header and footer template will be repeated for every feature in query result. It’s probably not too difficult to tweak the template to achieve what you want, but I don’t see a clean solution if I just want one header and footer template for all layers. Another thing I didn’t figure out is how to loop through each feature in query result in a more generic way instead of hard code the attribute name in each layer. I think it’s a very simple and typical work flow but I just don’t know how to do it in MapServer. Currently what I did for my “pizzastores” point layer (other two layers have similar but separate templates too) is like below:&lt;br /&gt;&lt;br /&gt;header template for “pizzastores” layer:&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: &lt;span style="color: green;"&gt;&amp;lt;!-- MapServer Template --&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2: &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/transitional.dtd"&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3: &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;html&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:   &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;head&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:     &lt;span style="color: green;"&gt;&amp;lt;!-- enforce the client to display result html as UTF-8 encoding --&amp;gt;&lt;/span&gt;  &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;meta&lt;/span&gt; &lt;span style="color: red;"&gt;http&lt;/span&gt;-&lt;span style="color: red;"&gt;equiv&lt;/span&gt;=&lt;span style="color: blue;"&gt;"content-type"&lt;/span&gt; &lt;span style="color: red;"&gt;content&lt;/span&gt;=&lt;span style="color: blue;"&gt;"text/html; charset=UTF-8"&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;meta&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;style&lt;/span&gt; &lt;span style="color: red;"&gt;type&lt;/span&gt;=&lt;span style="color: blue;"&gt;"text/css"&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8:       table, th, td {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9:         border:1px solid #e5e5e5;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;10:         border-collapse:collapse;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;11:         font-family: arial;          &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;12:         font-size: 80%;            &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;13:         color: #333333&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;14:       }             &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;15:       th, td {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;16:         valign: top;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;17:         text-align: center;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;18:       }          &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;19:       th {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;20:         background-color: #aed7ff&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;21:       }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;22:       caption {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;23:         border:1px solid #e5e5e5;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;24:         border-collapse:collapse;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;25:         font-family: arial;          &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;26:         font-weight: bold;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;27:         font-size: 80%;      &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;28:         text-align: left;      &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;29:         color: #333333;        &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;30:       }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;31:     &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;style&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;32:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;title&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;GetFeatureInfo Response&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;title&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;33:     &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;34:   &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;head&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;35:   &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;body&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;36:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;table&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;37:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;caption&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;layer names: pizzastores&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;caption&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;38:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;tbody&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;39:         &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;th&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;Layer Name&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;th&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;40:         &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;th&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;NAME&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;th&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;41:         &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;th&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;ADDRESS&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;th&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;42:         &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;th&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;TYPE&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;th&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;  &lt;/pre&gt;&lt;/pre&gt;content template for “pizzastores” layer&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: &lt;span style="color: green;"&gt;&amp;lt;!-- MapServer Template --&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;tr&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;td&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;Pizzastores&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;td&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;td&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;[item name=NAME format=$value escape=none]&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;td&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;td&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;[item name=ADDRESS format=$value escape=none]&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;td&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;td&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;[item name=TYPE format=$value escape=none]&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;td&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7:     &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;tr&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/pre&gt;footer layer for “pizzastores” layer&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: &lt;span style="color: green;"&gt;&amp;lt;!-- MapServer Template --&amp;gt;&lt;/span&gt;    &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2:       &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;tbody&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:     &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;table&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;br&lt;/span&gt;&lt;span style="color: blue;"&gt;/&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:   &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;body&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6: &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;html&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/pre&gt;You notice that in content template for the layer I can only access the current single query result which makes me think the templates will be repeatedly called for each feature in query results. I got the result below in browser but I have multiple &amp;lt;html&amp;gt; tags in the source:&lt;br /&gt;&lt;div style="text-align: left;"&gt;&lt;a href="http://lh3.ggpht.com/_qgvb6YVf5Fw/S1Dwlzq7aqI/AAAAAAAAAG8/FpZqTDni9uY/s1600-h/r%5B4%5D.png"&gt;&lt;img alt="r" border="0" height="257" src="http://lh3.ggpht.com/_qgvb6YVf5Fw/S1DwmLGuAqI/AAAAAAAAAHA/Dps2U63LPxA/r_thumb%5B2%5D.png?imgmax=800" style="border: 0px none; display: block; float: none; margin-left: auto; margin-right: auto;" title="r" width="353" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;The native GML format for WMS GetFeatureInfo response doesn’t have such issue though.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;GeoServer 2.0&lt;/b&gt;&lt;/u&gt;&lt;br /&gt;&lt;br /&gt;For GeoServer (latest released version 2.0.0), I found the work flow of customizing WMS GetFeatureInfo is very straight forward and smooth. All related information and samples are described in “&lt;a href="http://docs.geoserver.org/trunk/en/user/tutorials/GetFeatureInfo/index.html"&gt;GetFeatureInfo Templates&lt;/a&gt;” and “&lt;a href="http://docs.geoserver.org/trunk/en/user/tutorials/freemarker.html"&gt;Freemaker Templates&lt;/a&gt;” sections of the online user manual, which is neat. Similar to MapServer, GeoServer also uses the concept of header, footer and content html template (templates are with suffix .ftl which is just html with freemaker engine tags). There are two things I really like about GeoServer: (1) templates can be set at different levels like global, workspace (not tested though), datastore, layer so common header and footer template can be shared; (2) the content template is repeatedly applied for each feature collection (meaning all the query results from one layer) instead of each feature such that I can loop through each feature in a generic way which in the end reduces the number of templates I need. &lt;br /&gt;&lt;br /&gt;The only place I got trapped is that the online documentation is up to date enough the reflect the data folder structure change introduced in GeoServer 2.0.0. The old “&lt;b&gt;featuretypes&lt;/b&gt;” folder is gone (“&lt;b&gt;workspaces&lt;/b&gt;” folder is replacing it) but the online manual has a lot of places pointing to it.&lt;br /&gt;&lt;br /&gt;Here is what I did for geoserver:&lt;br /&gt;&lt;br /&gt;I created header.ftl and footer.ftl templates at global level and copy them to GEOSERVER_DATA_DIR\templates\ (create templates folder if it doesn’t exist):&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;html&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2:   &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;head&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;title&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;Geoserver GetFeatureInfo output&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;title&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:   &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;head&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:   &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;style&lt;/span&gt; &lt;span style="color: red;"&gt;type&lt;/span&gt;=&lt;span style="color: blue;"&gt;"text/css"&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6:         table, th, td {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7:       border:1px solid #e5e5e5;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8:       border-collapse:collapse;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9:       font-family: arial;          &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;10:       font-size: 80%;            &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;11:       color: #333333&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;12:     }             &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;13:     th, td {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;14:       valign: top;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;15:       text-align: center;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;16:     }          &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;17:     th {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;18:       background-color: #aed7ff&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;19:     }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;20:     caption {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;21:       border:1px solid #e5e5e5;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;22:       border-collapse:collapse;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;23:       font-family: arial;          &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;24:       font-weight: bold;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;25:       font-size: 80%;      &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;26:       text-align: left;      &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;27:       color: #333333;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;28:       &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;29:     }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;30:   &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;style&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;31:   &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;body&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1:   &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;body&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2: &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;html&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3: &lt;/pre&gt;&lt;/pre&gt;I created content.ftl template at datastore level which is copied to GEOSERVER_DATA_DIR\workspaces\&amp;lt;my_workspace&amp;gt;\&amp;lt;my_datastore&amp;gt;\&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;table&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2:   &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;caption&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;layer names: ${type.name}&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;caption&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:   &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;tr&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:   &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;th&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;fid&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;th&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;#list type.attributes as attribute&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;#if !attribute.isGeometry&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;th&lt;/span&gt; &lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;${attribute.name}&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;th&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8:       &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;#if&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9:     &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;#list&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;10:   &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;tr&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;11: &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;12:   &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;#assign odd=false&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;13:   &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;#list features as feature&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;14:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;#if odd&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;15:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;tr&lt;/span&gt; &lt;span style="color: red;"&gt;class&lt;/span&gt;=&lt;span style="color: blue;"&gt;"odd"&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;16:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;#else&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;17:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;tr&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;18:     &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;#if&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;19:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;#assign odd=!odd&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;20:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;td&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;${feature.fid}&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;td&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;    &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;21:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;#list feature.attributes as attribute&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;22:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;#if !attribute.isGeometry&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;23:         &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;td&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;${attribute.value?string}&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;td&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;24:       &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;#if&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;25:     &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;#list&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;26:     &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;tr&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;27:   &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;#list&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;28: &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;table&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;29: &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;br&lt;/span&gt;&lt;span style="color: blue;"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/pre&gt;New templates seem to require a restart of GeoServer and after that I get GetFeatureInfo response displayed in browser like below:&lt;br /&gt;&lt;div style="text-align: left;"&gt;&lt;a href="http://lh5.ggpht.com/_qgvb6YVf5Fw/S1DwmWS_tBI/AAAAAAAAAHE/FQusU_N9GCo/s1600-h/r%5B8%5D.png"&gt;&lt;img alt="r" border="0" height="201" src="http://lh3.ggpht.com/_qgvb6YVf5Fw/S1DwmuaILuI/AAAAAAAAAHI/K4tZMzAYFhg/r_thumb%5B4%5D.png?imgmax=800" style="border: 0px none; display: block; float: none; margin-left: auto; margin-right: auto;" title="r" width="644" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4314202365991499343-480627471363581629?l=augusttown.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://augusttown.blogspot.com/feeds/480627471363581629/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://augusttown.blogspot.com/2010/01/customize-wms-getfeatureinfo-response.html#comment-form' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/480627471363581629'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/480627471363581629'/><link rel='alternate' type='text/html' href='http://augusttown.blogspot.com/2010/01/customize-wms-getfeatureinfo-response.html' title='Customize WMS GetFeatureInfo response :: GeoServer 2.0 versus MapServer 5.4.2'/><author><name>Yingqi Tang</name><uri>http://www.blogger.com/profile/00422288991527433117</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/--JG8BCe4IgI/Tja7MbVf-uI/AAAAAAAAAYQ/nde6VIRt3aY/s220/august_and_me.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_qgvb6YVf5Fw/S1Dwk_-vYGI/AAAAAAAAAGw/77H7D3d5jcA/s72-c/sf_thumb3.png?imgmax=800' height='72' width='72'/><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4314202365991499343.post-5252805584998636450</id><published>2010-01-12T08:56:00.001-08:00</published><updated>2010-01-13T09:03:27.156-08:00</updated><title type='text'>Extend GeoServer with customized OWS service :: part 5</title><content type='html'>In &lt;a href="http://augusttown.blogspot.com/2010/01/extend-geoserver-with-customized-ows_09.html"&gt;previous post&lt;/a&gt; of this series, I summarize the basic work flow of GeoServer OWS dispatcher dispatching OWS requests based on request parameters “service”, “version”, and “request”. We already know that in “ags-ows” service project, the “export” method will finally be called to process the request, and it takes a request bean (AgsOwsExportRequest) instance as input parameter. So in this post let’s pick up from that point and have a look at how response is produced.&lt;br /&gt;&lt;br /&gt;We already know ows dispatcher dispatches requests, but after it finds and calls the appropriate method in appropriate service class with request bean it doesn’t stop it job. Instead it keeps doing its job to produce the response. &lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2: &lt;span style="color: green;"&gt;// this is where OWS dispatcher finds the service and method and execute it&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3: &lt;span style="color: green;"&gt;// execute it&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4: Object result = execute(request, operation);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5: &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6: &lt;span style="color: green;"&gt;// this is where OWS dispatcher keeps its job to produce response&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7: &lt;span style="color: green;"&gt;//write the response&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8: &lt;span style="color: blue;"&gt;if&lt;/span&gt; (result != &lt;span style="color: blue;"&gt;null&lt;/span&gt;) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9:     response(result, request, operation);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;10: }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;11: ...&lt;/pre&gt;&lt;/pre&gt;The code snippet above is from OWS dispatcher, the “execute” method actually calls the service operation method (in case of “ags-ows” service, it is the export method in AarcGISServerOWSService class) and return whatever it returns, which will be assigned to “result”. Look at export method again &lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: &lt;span style="color: green;"&gt;// export method in ArcGISServerOWSService class&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2: ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3: &lt;span style="color: blue;"&gt;public&lt;/span&gt; AgsOwsExportResponse export(AgsOwsExportRequest request) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: yellow; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:     &lt;span style="color: blue;"&gt;return&lt;/span&gt; &lt;span style="color: blue;"&gt;new&lt;/span&gt; AgsOwsExportResponse(&lt;span style="color: blue;"&gt;this&lt;/span&gt;.geoServer);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5: }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6: ...&lt;/pre&gt;&lt;/pre&gt;the result is actually in type of &lt;b&gt;AgsOwsExportResponse&lt;/b&gt; (you saw that in &lt;a href="http://augusttown.blogspot.com/2010/01/extend-geoserver-with-customized-ows_7162.html"&gt;part 2&lt;/a&gt; but I told you not to worry about then). AgsOwsExportResponse is a subclass of &lt;b&gt;org.geoserver.ows.Response&lt;/b&gt;, which is the core element for an OWS service to produce responses. Before we jump into the implementation of AgsOwsExportResponse, let’s first look at how ows dispatcher uses response class. Go back to dispatcher code again and step into the “response(result, request, operation);” method we saw before:&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2: &lt;span style="color: green;"&gt;// response method in org.geoserver.ows.Dispatcher class&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3: &lt;span style="color: blue;"&gt;void&lt;/span&gt; response(Object result, Request req, Operation opDescriptor)&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:     &lt;span style="color: blue;"&gt;throws&lt;/span&gt; Throwable {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5: &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6:     &lt;span style="color: green;"&gt;// loop through all registered subclass of org.geoserver.ows.Response &lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7:     &lt;span style="color: green;"&gt;//   in application context and try to match a unique response type based on&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8:     &lt;span style="color: green;"&gt;//   the class type of result and the binding class of Response subclass   &lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9: &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;10:     ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;11:     ... &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;12:     Response response = (Response) responses.get(0);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;13:     ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;14:     &lt;span style="color: green;"&gt;// you need to implement getMimeType() in Response subclass &lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;15:     req.httpResponse.setContentType(response.getMimeType(result, opDescriptor));&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;16:     ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;17:     &lt;span style="color: green;"&gt;// you need to implement write() in Response subclass&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;18:     response.write(result, output, opDescriptor);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;19:     ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;20:     &lt;span style="color: green;"&gt;// finally flush out the response to clients&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;21:     req.httpResponse.getOutputStream().flush();&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;22: }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;23: ...&lt;/pre&gt;&lt;/pre&gt;Dispatcher will loop through all the subclasses of org.geoserver.ows.Response registered in application context and find a unique response class whose binding class matches the class type of result object returned by service operation method. Take AgsOwsExportResponse as an example, since it’s binding class is itself which exactly matches the result object returned by export method of ArcGISServerOWSService class, it will be picked up by dispatcher.&lt;br /&gt;&lt;br /&gt;After dispatcher locates the appropriate response class, two methods actually matters and thus must be implemented. (1) &lt;b&gt;getMimeType()&lt;/b&gt;, which returns mime type string of your response, and it will be set directly in the header of http response back to clients; (2) &lt;b&gt;write()&lt;/b&gt;, which writes response body in output stream of http response back to clients. Below is a sample implementation of AgsOwsExportResponse class and how it is registered in application context:&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;class&lt;/span&gt; AgsOwsExportResponse &lt;span style="color: blue;"&gt;extends&lt;/span&gt; Response {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2: &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:     &lt;span style="color: blue;"&gt;public&lt;/span&gt; AgsOwsExportResponse() {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:         &lt;span style="color: blue;"&gt;super&lt;/span&gt;(AgsOwsExportResponse.&lt;span style="color: blue;"&gt;class&lt;/span&gt;);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:     }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6: &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7:     @Override&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8:     &lt;span style="color: blue;"&gt;public&lt;/span&gt; String getMimeType(Object value, Operation operation)&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9:         &lt;span style="color: blue;"&gt;throws&lt;/span&gt; ServiceException {  &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;10:         &lt;span style="color: green;"&gt;// to simplify the problem, always return image/png&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;11:         &lt;span style="color: green;"&gt;// but in your own ows service, you can decided based on what you support &lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;12:         &lt;span style="color: green;"&gt;//    and what clients request&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;13:         &lt;span style="color: blue;"&gt;return&lt;/span&gt; "&lt;span style="color: darkred;"&gt;image/png&lt;/span&gt;";&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;14:     }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;15:     ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;16:     @Override&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;17:     &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;void&lt;/span&gt; write(Object value, OutputStream output, Operation operation)&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;18:         &lt;span style="color: blue;"&gt;throws&lt;/span&gt; IOException, ServiceException {  &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;19:         ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;20:         &lt;span style="color: green;"&gt;// write response map image back to clients &lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;21:         ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;22:     }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;23: }&lt;/pre&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: &lt;span style="color: green;"&gt;&amp;lt;!-- service operation response --&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2: &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;bean&lt;/span&gt; &lt;span style="color: red;"&gt;id&lt;/span&gt;=&lt;span style="color: blue;"&gt;"agsOwsCapabilitiesResponse"&lt;/span&gt; &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:     &lt;span style="color: red;"&gt;class&lt;/span&gt;=&lt;span style="color: blue;"&gt;"org.geoserver.ows.arcgisserver.responses.AgsOwsCapabilitiesResponse"&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:     &lt;span style="color: red;"&gt;singleton&lt;/span&gt;=&lt;span style="color: blue;"&gt;"false"&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;     &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;constructor&lt;/span&gt;-&lt;span style="color: red;"&gt;arg&lt;/span&gt; &lt;span style="color: red;"&gt;ref&lt;/span&gt;=&lt;span style="color: blue;"&gt;"geoServer"&lt;/span&gt;&lt;span style="color: blue;"&gt;/&amp;gt;&lt;/span&gt;   &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6: &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;bean&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/pre&gt;Notice the binding class is AgsOwsExportResponse itself but it could definitely be something else if the service operation method export chooses to return something else (org.geoserver.wcs.responses.GetCapabilitiesResponse is a good example of that). As I mentioned in the beginning, “image/png” is hard coded in getMimeType() to simplify the problem. And finally in write() I need to produce a map image and write it in output stream. The implementation of write() method deserves another separate paragraph of explanation which I will do right after, and so far ows dispatcher has finished the whole life cycle for an ows service request dispatching.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;u&gt;Implement Write() in AgsOwsExportResponse and AgsOwsMapProducer&lt;/u&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Implementing Write() in AgsOwsExportResponse can be as simple as less than 10 lines of code if you only want to produce and output some simple static content back to clients.&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: &lt;span style="color: blue;"&gt;private&lt;/span&gt; &lt;span style="color: blue;"&gt;void&lt;/span&gt; write(Object value, OutputStream output, Operation operation) &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2:     &lt;span style="color: blue;"&gt;throws&lt;/span&gt; IOException, ServiceException {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:     String responseStr = "&lt;span style="color: darkred;"&gt;Static content&lt;/span&gt;";&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:     OutputStreamWriter writer = &lt;span style="color: blue;"&gt;new&lt;/span&gt; OutputStreamWriter(output);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:     writer.write(responseStr);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6:     writer.flush();&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7: }&lt;/pre&gt;&lt;/pre&gt;But if it is creating a map image, it can definitely become way more complicated (org.vfny.geoserver.wms.responses.GetMapResponse in wms project is a good example), which sometimes needs one or more helper classes (those map producer classes under package org.vfny.geoserver.wms.responses.map are very good example). In AgsOwsExportResponse of “ags-ows” service project I sort of borrowed the idea from GeoServer WMS &lt;b&gt;GetMapResponse&lt;/b&gt; class and &lt;b&gt;PNGMapProducer &lt;/b&gt;class, in which a helper class similar to PNGMapProducer called &lt;b&gt;AgsOwsMapProducer&lt;/b&gt; is created to render map. Of course I omitted most of the details and keeps just enough code to create an map image in png format and write out to client.&lt;br /&gt;&lt;br /&gt;In the write() method of AgsOwsExportResponse, I create an instance of GraphicEnhancedMapContext class (from GeoTools library) and pass it to AgsOwsMapProducer. The GraphicEnhancedMapContext instance basically stores all information (e.g. map projection, background color, transparent, layers, styles etc.) needed to produce a map image using GeoTools.&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: &lt;span style="color: blue;"&gt;private&lt;/span&gt; &lt;span style="color: blue;"&gt;void&lt;/span&gt; write(Object value, OutputStream output, Operation operation)&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2:       &lt;span style="color: blue;"&gt;throws&lt;/span&gt; IOException, ServiceException {    &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:     &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:     AgsOwsExportResponse exportResponse = (AgsOwsExportResponse)value;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:     AgsOwsExportRequest exportRequest &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6:         = (AgsOwsExportRequest)OwsUtils.parameter(operation.getParameters(), AgsOwsExportRequest.&lt;span style="color: blue;"&gt;class&lt;/span&gt;);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7:     &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8:     &lt;span style="color: green;"&gt;/*&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9:      * the main purpose here is to create mapContext {GraphicEnhancedMapContext} -&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;10:      * - and pass it to map producer to generate the map&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;11:      * - borrow the idea from GetMapResponse but omitted a lot details&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;12:      */    &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;13:     &lt;span style="color: green;"&gt;// requested image crs&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;14:     &lt;span style="color: blue;"&gt;final&lt;/span&gt; CoordinateReferenceSystem imageSR = exportRequest.getImageSR();&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;15:     &lt;span style="color: green;"&gt;// requested layers&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;16:     &lt;span style="color: blue;"&gt;final&lt;/span&gt; LayerInfo[] layers = exportRequest.getLayers();          &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;17:     &lt;span style="color: green;"&gt;// initialize mapContext&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;18:     &lt;span style="color: blue;"&gt;this&lt;/span&gt;.mapContext = &lt;span style="color: blue;"&gt;new&lt;/span&gt; GraphicEnhancedMapContext();    &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;19:     &lt;span style="color: blue;"&gt;try&lt;/span&gt; {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;20:       &lt;span style="color: blue;"&gt;this&lt;/span&gt;.mapContext.setCoordinateReferenceSystem(imageSR);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;21:     } &lt;span style="color: blue;"&gt;catch&lt;/span&gt;(FactoryException e) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;22:       e.printStackTrace();&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;23:     } &lt;span style="color: blue;"&gt;catch&lt;/span&gt;(TransformException e) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;24:       e.printStackTrace();&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;25:     }        &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;26:     &lt;span style="color: green;"&gt;// bbox and bbox crs&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;27:     &lt;span style="color: blue;"&gt;final&lt;/span&gt; CoordinateReferenceSystem bboxSR = exportRequest.getBboxSR();&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;28:     &lt;span style="color: blue;"&gt;final&lt;/span&gt; Envelope bbox = exportRequest.getBbox();&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;29:     &lt;span style="color: blue;"&gt;if&lt;/span&gt;(bboxSR != &lt;span style="color: blue;"&gt;null&lt;/span&gt;) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;30:       &lt;span style="color: blue;"&gt;this&lt;/span&gt;.mapContext.setAreaOfInterest(bbox, bboxSR);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;31:     } &lt;span style="color: blue;"&gt;else&lt;/span&gt; {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;32:       &lt;span style="color: green;"&gt;// TODO: should throw exception, no?&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;33:       &lt;span style="color: blue;"&gt;this&lt;/span&gt;.mapContext.setAreaOfInterest(bbox, DefaultGeographicCRS.WGS84);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;34:     }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;35:     &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;36:     &lt;span style="color: blue;"&gt;this&lt;/span&gt;.mapContext.setMapWidth(exportRequest.getWidth());&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;37:     &lt;span style="color: blue;"&gt;this&lt;/span&gt;.mapContext.setMapHeight(exportRequest.getHeight());&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;38:     &lt;span style="color: blue;"&gt;this&lt;/span&gt;.mapContext.setBgColor(exportRequest.getBgColor());&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;39:     &lt;span style="color: blue;"&gt;this&lt;/span&gt;.mapContext.setTransparent(exportRequest.isTransparent());&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;40:     &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;41:     &lt;span style="color: blue;"&gt;try&lt;/span&gt; {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;42:       &lt;span style="color: blue;"&gt;for&lt;/span&gt; (&lt;span style="color: blue;"&gt;int&lt;/span&gt; i=0; i&amp;lt;layers.length; i++) {      &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;43:         &lt;span style="color: blue;"&gt;final&lt;/span&gt; Style layerStyle = layers[i].getDefaultStyle().getStyle();        &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;44:         &lt;span style="color: blue;"&gt;final&lt;/span&gt; MapLayer layer;                &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;45:         &lt;span style="color: blue;"&gt;if&lt;/span&gt;(layers[i].getType().getCode() == LayerInfo.Type.VECTOR.getCode()) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;46:           FeatureSource&amp;lt;? &lt;span style="color: blue;"&gt;extends&lt;/span&gt; FeatureType, ? &lt;span style="color: blue;"&gt;extends&lt;/span&gt; Feature&amp;gt; featureSource;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;47:           FeatureTypeInfo resource = (FeatureTypeInfo)layers[i].getResource();&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;48:           &lt;span style="color: blue;"&gt;if&lt;/span&gt;(resource.getStore() == &lt;span style="color: blue;"&gt;null&lt;/span&gt; || resource.getStore().getDataStore(&lt;span style="color: blue;"&gt;null&lt;/span&gt;) == &lt;span style="color: blue;"&gt;null&lt;/span&gt;) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;49:                   &lt;span style="color: blue;"&gt;throw&lt;/span&gt; &lt;span style="color: blue;"&gt;new&lt;/span&gt; IOException("&lt;span style="color: darkred;"&gt;&lt;/span&gt;");&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;50:               }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;51:           Hints hints = &lt;span style="color: blue;"&gt;new&lt;/span&gt; Hints(ResourcePool.REPROJECT, Boolean.valueOf(&lt;span style="color: blue;"&gt;false&lt;/span&gt;));&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;52:           featureSource = resource.getFeatureSource(&lt;span style="color: blue;"&gt;null&lt;/span&gt;, hints);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;53:           &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;54:           layer = &lt;span style="color: blue;"&gt;new&lt;/span&gt; FeatureSourceMapLayer(featureSource, layerStyle);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;55:                     layer.setTitle(layers[i].getResource().getName());                    &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;56:                     &lt;span style="color: green;"&gt;// use default filter and version in DefaultQuery&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;57:                     &lt;span style="color: blue;"&gt;final&lt;/span&gt; DefaultQuery definitionQuery = &lt;span style="color: blue;"&gt;new&lt;/span&gt; DefaultQuery(featureSource.getSchema().getName().getLocalPart());                                      &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;58:                     layer.setQuery(definitionQuery);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;59:                     mapContext.addLayer(layer);                    &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;60:         } &lt;span style="color: blue;"&gt;else&lt;/span&gt; &lt;span style="color: blue;"&gt;if&lt;/span&gt;(layers[i].getType().getCode() == LayerInfo.Type.RASTER.getCode()) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;61:           &lt;span style="color: green;"&gt;//&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;62:         }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;63:         &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;64:         &lt;span style="color: green;"&gt;// default outputFormat to 'png' and mimeType to 'image/png'&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: yellow; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;65:         &lt;span style="color: blue;"&gt;this&lt;/span&gt;.mapProducer = &lt;span style="color: blue;"&gt;new&lt;/span&gt; AgsOwsMapProducer();&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: yellow; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;66:         &lt;span style="color: blue;"&gt;this&lt;/span&gt;.mapProducer.setMapContext(&lt;span style="color: blue;"&gt;this&lt;/span&gt;.mapContext);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: yellow; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;67:         &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: yellow; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;68:         &lt;span style="color: blue;"&gt;this&lt;/span&gt;.mapProducer.produceMap();&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: yellow; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;69:         &lt;span style="color: blue;"&gt;this&lt;/span&gt;.mapProducer.writeTo(output);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;70:       }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;71:     } &lt;span style="color: blue;"&gt;catch&lt;/span&gt;(Exception e) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;72:       e.printStackTrace();&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;73:     }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;74:   }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;75: &lt;/pre&gt;&lt;/pre&gt;And AgsOwsMapProducer, which takes the map context, finally renders a map using GeoTools StreamingRenderer by calling produceMap() and write out image stream by calling writeTo().&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2: &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;void&lt;/span&gt; produceMap() &lt;span style="color: blue;"&gt;throws&lt;/span&gt; Exception {  &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:     ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:     Rectangle paintArea &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:         = &lt;span style="color: blue;"&gt;new&lt;/span&gt; Rectangle(0, 0, mapContext.getMapWidth(), mapContext.getMapHeight());&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6:     String antialias = "&lt;span style="color: darkred;"&gt;none&lt;/span&gt;";&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7:     IndexColorModel palette = &lt;span style="color: blue;"&gt;null&lt;/span&gt;;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8:     ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9:     &lt;span style="color: blue;"&gt;boolean&lt;/span&gt; useAlpha = &lt;span style="color: blue;"&gt;true&lt;/span&gt;;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;10:     &lt;span style="color: blue;"&gt;final&lt;/span&gt; &lt;span style="color: blue;"&gt;boolean&lt;/span&gt; transparent = mapContext.isTransparent();&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;11:     ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;12:     &lt;span style="color: blue;"&gt;final&lt;/span&gt; Color bgColor = mapContext.getBgColor();&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;13:     ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;14:     &lt;span style="color: blue;"&gt;final&lt;/span&gt; RenderedImage preparedImage &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;15:         = ImageUtils.createImage(paintArea.width, paintArea.height, palette, useAlpha);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;16:     ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;17:     &lt;span style="color: blue;"&gt;final&lt;/span&gt; Map&amp;lt;RenderingHints.Key, Object&amp;gt; hintsMap &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;18:         = &lt;span style="color: blue;"&gt;new&lt;/span&gt; HashMap&amp;lt;RenderingHints.Key, Object&amp;gt;();      &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;19:     &lt;span style="color: blue;"&gt;final&lt;/span&gt; Graphics2D graphic &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;20:         = ImageUtils.prepareTransparency(transparent, bgColor, preparedImage, hintsMap);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;21:     ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;22:     graphic.setRenderingHints(hintsMap);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;23:     ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;24:     StreamingRenderer renderer = &lt;span style="color: blue;"&gt;new&lt;/span&gt; StreamingRenderer();      &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;25:     renderer = &lt;span style="color: blue;"&gt;new&lt;/span&gt; StreamingRenderer();        &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;26:     renderer.setContext(mapContext);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;27:       &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;28:     RenderingHints hints = &lt;span style="color: blue;"&gt;new&lt;/span&gt; RenderingHints(hintsMap);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;29:     renderer.setJava2DHints(hints);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;30:               &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;31:     Map&amp;lt;Object, Object&amp;gt; rendererParams = &lt;span style="color: blue;"&gt;new&lt;/span&gt; HashMap&amp;lt;Object, Object&amp;gt;();&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;32:     rendererParams.put("&lt;span style="color: darkred;"&gt;optimizedDataLoadingEnabled&lt;/span&gt;", &lt;span style="color: blue;"&gt;new&lt;/span&gt; Boolean(&lt;span style="color: blue;"&gt;true&lt;/span&gt;));    &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;33:     rendererParams.put("&lt;span style="color: darkred;"&gt;maxFiltersToSendToDatastore&lt;/span&gt;",  DefaultWebMapService.getMaxFilterRules());&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;34:     rendererParams.put(ShapefileRenderer.SCALE_COMPUTATION_METHOD_KEY, ShapefileRenderer.SCALE_OGC);    &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;35:     ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;36:     rendererParams.put(StreamingRenderer.ADVANCED_PROJECTION_HANDLING_KEY, &lt;span style="color: blue;"&gt;true&lt;/span&gt;);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;37:     ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;38:     renderer.setRendererHints(rendererParams);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;39:     &lt;span style="color: blue;"&gt;try&lt;/span&gt; {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;40:         &lt;span style="color: blue;"&gt;final&lt;/span&gt; ReferencedEnvelope dataArea = mapContext.getAreaOfInterest();&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;41:         renderer.paint(graphic, paintArea, dataArea);        &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;42:     } &lt;span style="color: blue;"&gt;finally&lt;/span&gt; {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;43:         graphic.dispose();&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;44:     }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;45:     ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;46:     &lt;span style="color: blue;"&gt;this&lt;/span&gt;.image = preparedImage;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;47: }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;48: ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;49: &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;void&lt;/span&gt; writeTo(OutputStream out) &lt;span style="color: blue;"&gt;throws&lt;/span&gt; ServiceException, java.io.IOException {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;50:     ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;51:     &lt;span style="color: blue;"&gt;final&lt;/span&gt; String format = getOutputFormat();&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;52:     ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;53:     &lt;span style="color: blue;"&gt;new&lt;/span&gt; ImageWorker(image).writePNG(&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;54:         outStream, &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;55:         "&lt;span style="color: darkred;"&gt;FILTERED&lt;/span&gt;", &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;56:         100.0f,  &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;57:         &lt;span style="color: blue;"&gt;true&lt;/span&gt;,&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;58:         image.getColorModel() &lt;span style="color: blue;"&gt;instanceof&lt;/span&gt; IndexColorModel&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;59:     );&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;60:     ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;61: }&lt;/pre&gt;&lt;/pre&gt;&lt;span style="font-size: xx-small;"&gt;Note: the sample above for both produceMap() and writeTo() omitted a lot of details which can not be simply copied and paste to be compiled. But I will provide the link to download the sample code in the end of the series.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;u&gt;Wrap it up&lt;/u&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;So far I’ve finished everything I planned for this little “ags-ows” project. So just restart GeoServer and the customized OWS service “ags-ows” will be ready to serve out map. Here are two sample requests and result maps:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://localhost:8080/geoserver/ows?service=agsows&amp;amp;request=export&amp;amp;version=1.0.0&amp;amp;bbox=-180,0,0,90&amp;amp;transparent=true&amp;amp;size=512,256&amp;amp;bboxSR=4326&amp;amp;imageSR=4326&amp;amp;layers=show:states" title="http://sazabi:9090/geoserver/ows?service=agsows&amp;amp;request=export&amp;amp;version=1.0.0&amp;amp;bbox=-180,0,0,90&amp;amp;transparent=true&amp;amp;size=512,256&amp;amp;bboxSR=4326&amp;amp;imageSR=4326&amp;amp;layers=show:states"&gt;http://localhost:8080/geoserver/ows?service=agsows&amp;amp;request=export&amp;amp;version=1.0.0&amp;amp;bbox=-180,0,0,90&amp;amp;transparent=true&amp;amp;size=512,256&amp;amp;bboxSR=4326&amp;amp;imageSR=4326&amp;amp;layers=show:states&lt;/a&gt;&lt;br /&gt;&lt;a href="http://lh3.ggpht.com/_qgvb6YVf5Fw/S0ypnTjVRkI/AAAAAAAAAGc/roqVLnmfOws/s1600-h/wgs84%5B8%5D.png"&gt;&lt;img alt="wgs84" border="0" height="276" src="http://lh6.ggpht.com/_qgvb6YVf5Fw/S0ypn-dGK7I/AAAAAAAAAGg/-9QM6ufApmQ/wgs84_thumb%5B6%5D.png?imgmax=800" style="border: 0px none; display: inline;" title="wgs84" width="516" /&gt;&lt;/a&gt; &lt;br /&gt;&lt;a href="http://localhost:8080/geoserver/ows?service=agsows&amp;amp;request=export&amp;amp;version=1.0.0&amp;amp;bbox=-20037508.34,-20037508.34,20037508.34,20037508.34&amp;amp;transparent=true&amp;amp;size=512,512&amp;amp;bboxSR=900913&amp;amp;imageSR=900913&amp;amp;layers=show:states" title="http://sazabi:9090/geoserver/ows?service=agsows&amp;amp;request=export&amp;amp;version=1.0.0&amp;amp;bbox=-20037508.34,-20037508.34,20037508.34,20037508.34&amp;amp;transparent=true&amp;amp;size=512,512&amp;amp;bboxSR=900913&amp;amp;imageSR=900913&amp;amp;layers=show:states"&gt;http://localhost:8080/geoserver/ows?service=agsows&amp;amp;request=export&amp;amp;version=1.0.0&amp;amp;bbox=-20037508.34,-20037508.34,20037508.34,20037508.34&amp;amp;transparent=true&amp;amp;size=512,512&amp;amp;bboxSR=900913&amp;amp;imageSR=900913&amp;amp;layers=show:states&lt;/a&gt;&lt;br /&gt;&lt;a href="http://lh6.ggpht.com/_qgvb6YVf5Fw/S0ypoMQhstI/AAAAAAAAAGk/neD5QvqaUag/s1600-h/mercator%5B3%5D.png"&gt;&lt;img alt="mercator" border="0" height="516" src="http://lh3.ggpht.com/_qgvb6YVf5Fw/S0ypoYFpoHI/AAAAAAAAAGo/pNJWUlV_GHs/mercator_thumb%5B1%5D.png?imgmax=800" style="border: 0px none; display: inline;" title="mercator" width="516" /&gt;&lt;/a&gt; &lt;br /&gt;&lt;br /&gt;This is the final part of the whole series and the source code covered in my little "ags-ows" project is packaged as &lt;b&gt;geoserver-playground-ags-ows.zip&lt;/b&gt;, which can be downloaded from &lt;a href="http://august-resources.appspot.com/downloads/geoserver-playground-ags-ows.zip"&gt;here&lt;/a&gt;. Feel free to leave any feedback and comments.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4314202365991499343-5252805584998636450?l=augusttown.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://augusttown.blogspot.com/feeds/5252805584998636450/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://augusttown.blogspot.com/2010/01/extend-geoserver-with-customized-ows_12.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/5252805584998636450'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/5252805584998636450'/><link rel='alternate' type='text/html' href='http://augusttown.blogspot.com/2010/01/extend-geoserver-with-customized-ows_12.html' title='Extend GeoServer with customized OWS service :: part 5'/><author><name>Yingqi Tang</name><uri>http://www.blogger.com/profile/00422288991527433117</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/--JG8BCe4IgI/Tja7MbVf-uI/AAAAAAAAAYQ/nde6VIRt3aY/s220/august_and_me.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_qgvb6YVf5Fw/S0ypn-dGK7I/AAAAAAAAAGg/-9QM6ufApmQ/s72-c/wgs84_thumb%5B6%5D.png?imgmax=800' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4314202365991499343.post-7186171563439773901</id><published>2010-01-09T19:32:00.001-08:00</published><updated>2010-01-10T10:08:17.663-08:00</updated><title type='text'>Extend GeoServer with customized OWS service :: part 4</title><content type='html'>In &lt;a href="http://augusttown.blogspot.com/2010/01/extend-geoserver-with-customized-ows_08.html"&gt;previous post&lt;/a&gt; of this series I created request bean class AgsOwsExportRequest for “ags-ows” service and helper class AgsOwsExportRequestKvpReader to populate request bean with parsed value objects from kvp parsers. Now it’s time to have a close look at how OWS dispatcher exactly dispatches requests and produces responses.&lt;br /&gt;&lt;br /&gt;In &lt;a href="http://augusttown.blogspot.com/2010/01/extend-geoserver-with-customized-ows_7162.html"&gt;part 2&lt;/a&gt; I briefly mentioned GeoServer’s OWS dispather (org.geoserver.ows.Dispatcher), which is also re-used by “ags-ows” service to handle requests. So what really happens under the hood? GeoServer as J2EE application is built upon Java Servlet technology and Spring framework (Wicket was introduced later for the web UI). No matter whether you start GeoServer in built Jetty web server or deploy it in other containers like Tomcat, the start point is always the &lt;b&gt;DispatcherServlet&lt;/b&gt; (org.springframework.web.servlet.DispatcherServlet) declared in the “web-app” project of GeoServer. So if you look at the “web.xml” in “web-app” project, you will find following servlet definition:&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-&lt;span style="color: red;"&gt;name&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;dispatcher&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-name&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-&lt;span style="color: red;"&gt;class&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: yellow; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:           org.springframework.web.servlet.DispatcherServlet&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:       &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-class&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6: &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/pre&gt;you will also find following servlet mapping for different GeoServer url patterns as below:&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-&lt;span style="color: red;"&gt;mapping&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2:         &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-&lt;span style="color: red;"&gt;name&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;dispatcher&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-name&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:         &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;url&lt;/span&gt;-&lt;span style="color: red;"&gt;pattern&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;/web/*&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;url&lt;/span&gt;-pattern&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:     &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-mapping&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-&lt;span style="color: red;"&gt;mapping&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-&lt;span style="color: red;"&gt;name&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;dispatcher&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-name&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;url&lt;/span&gt;-&lt;span style="color: red;"&gt;pattern&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;/rest/*&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;url&lt;/span&gt;-pattern&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8:     &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-mapping&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-&lt;span style="color: red;"&gt;mapping&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;10:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-&lt;span style="color: red;"&gt;name&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;dispatcher&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-name&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;11:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;url&lt;/span&gt;-&lt;span style="color: red;"&gt;pattern&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;/security/*&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;url&lt;/span&gt;-pattern&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;12:     &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-mapping&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;    &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;13:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-&lt;span style="color: red;"&gt;mapping&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;14:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-&lt;span style="color: red;"&gt;name&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;dispatcher&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-name&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;15:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;url&lt;/span&gt;-&lt;span style="color: red;"&gt;pattern&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;/wms/*&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;url&lt;/span&gt;-pattern&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;16:     &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-mapping&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;17:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-&lt;span style="color: red;"&gt;mapping&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;18:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-&lt;span style="color: red;"&gt;name&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;dispatcher&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-name&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;19:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;url&lt;/span&gt;-&lt;span style="color: red;"&gt;pattern&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;/wcs/*&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;url&lt;/span&gt;-pattern&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;20:     &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-mapping&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;21:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-&lt;span style="color: red;"&gt;mapping&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;22:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-&lt;span style="color: red;"&gt;name&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;dispatcher&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-name&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;23:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;url&lt;/span&gt;-&lt;span style="color: red;"&gt;pattern&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;/wfs/*&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;url&lt;/span&gt;-pattern&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;24:     &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-mapping&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;25:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-&lt;span style="color: red;"&gt;mapping&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;26:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-&lt;span style="color: red;"&gt;name&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;dispatcher&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-name&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;27:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;url&lt;/span&gt;-&lt;span style="color: red;"&gt;pattern&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;/ows/*&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;url&lt;/span&gt;-pattern&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;28:     &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-mapping&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;29:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-&lt;span style="color: red;"&gt;mapping&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;30:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-&lt;span style="color: red;"&gt;name&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;dispatcher&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-name&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;31:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;url&lt;/span&gt;-&lt;span style="color: red;"&gt;pattern&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;/wfsv/*&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;url&lt;/span&gt;-pattern&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;32:     &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-mapping&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;33:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-&lt;span style="color: red;"&gt;mapping&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;34:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-&lt;span style="color: red;"&gt;name&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;dispatcher&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-name&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;35:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;url&lt;/span&gt;-&lt;span style="color: red;"&gt;pattern&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;/wcs111/*&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;url&lt;/span&gt;-pattern&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;36:     &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-mapping&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;37:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-&lt;span style="color: red;"&gt;mapping&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;38:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-&lt;span style="color: red;"&gt;name&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;dispatcher&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-name&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;39:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;url&lt;/span&gt;-&lt;span style="color: red;"&gt;pattern&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;/kml/*&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;url&lt;/span&gt;-pattern&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;40:     &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-mapping&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;41:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-&lt;span style="color: red;"&gt;mapping&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;42:      &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-&lt;span style="color: red;"&gt;name&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;dispatcher&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-name&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;43:      &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;url&lt;/span&gt;-&lt;span style="color: red;"&gt;pattern&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;/styles/*&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;url&lt;/span&gt;-pattern&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;44:     &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-mapping&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;45:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-&lt;span style="color: red;"&gt;mapping&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;46:      &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-&lt;span style="color: red;"&gt;name&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;dispatcher&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-name&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;47:      &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;url&lt;/span&gt;-&lt;span style="color: red;"&gt;pattern&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;/www/*&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;url&lt;/span&gt;-pattern&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;48:     &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-mapping&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;49:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-&lt;span style="color: red;"&gt;mapping&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;50:      &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-&lt;span style="color: red;"&gt;name&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;dispatcher&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-name&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;51:      &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;url&lt;/span&gt;-&lt;span style="color: red;"&gt;pattern&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;/temp/*&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;url&lt;/span&gt;-pattern&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;52:     &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-mapping&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;53:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-&lt;span style="color: red;"&gt;mapping&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;54:      &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-&lt;span style="color: red;"&gt;name&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;dispatcher&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-name&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;55:      &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;url&lt;/span&gt;-&lt;span style="color: red;"&gt;pattern&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;/history/*&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;url&lt;/span&gt;-pattern&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;56:     &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-mapping&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;57:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-&lt;span style="color: red;"&gt;mapping&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;58:      &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-&lt;span style="color: red;"&gt;name&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;dispatcher&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-name&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;59:      &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;url&lt;/span&gt;-&lt;span style="color: red;"&gt;pattern&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;/gwc/*&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;url&lt;/span&gt;-pattern&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;60:     &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-mapping&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;61:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-&lt;span style="color: red;"&gt;mapping&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;62:      &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-&lt;span style="color: red;"&gt;name&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;dispatcher&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-name&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;63:      &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;url&lt;/span&gt;-&lt;span style="color: red;"&gt;pattern&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;/pdf/*&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;url&lt;/span&gt;-pattern&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;64:     &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;servlet&lt;/span&gt;-mapping&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/pre&gt;almost all types of requests will be handled by that Spring DispatcherServlet at the first place. According to Spring framework documentation, DispatcherServlet is: “&lt;i&gt;the central dispatcher for HTTP request handlers/controllers, e.g. for web UI controllers or HTTP-based remote service exporters. Dispatches to registered handlers for processing a web request, providing convenient mapping and exception handling facilities.&lt;/i&gt;” So HTTP requests that reach it will be dispatched to different sub-routine based on registered handlers, and as mentioned in &lt;a href="http://augusttown.blogspot.com/2010/01/extend-geoserver-with-customized-ows_7162.html"&gt;part 2&lt;/a&gt; OWS dispatcher is one of those registered handler (as a org.springframework.web.servlet.handler.SimpleUrlHandlerMapping):&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1:   &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;bean&lt;/span&gt; &lt;span style="color: red;"&gt;id&lt;/span&gt;=&lt;span style="color: blue;"&gt;"dispatcher"&lt;/span&gt; &lt;span style="color: red;"&gt;class&lt;/span&gt;=&lt;span style="color: blue;"&gt;"org.geoserver.ows.Dispatcher"&lt;/span&gt;&lt;span style="color: blue;"&gt;/&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2:   &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;bean&lt;/span&gt; &lt;span style="color: red;"&gt;id&lt;/span&gt;=&lt;span style="color: blue;"&gt;"dispatcherMapping"&lt;/span&gt; &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:             &lt;span style="color: red;"&gt;class&lt;/span&gt;=&lt;span style="color: blue;"&gt;"org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;property&lt;/span&gt; &lt;span style="color: red;"&gt;name&lt;/span&gt;=&lt;span style="color: blue;"&gt;"alwaysUseFullPath"&lt;/span&gt; &lt;span style="color: red;"&gt;value&lt;/span&gt;=&lt;span style="color: blue;"&gt;"true"&lt;/span&gt;&lt;span style="color: blue;"&gt;/&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;property&lt;/span&gt; &lt;span style="color: red;"&gt;name&lt;/span&gt;=&lt;span style="color: blue;"&gt;"mappings"&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;props&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7:         &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;prop&lt;/span&gt; &lt;span style="color: red;"&gt;key&lt;/span&gt;=&lt;span style="color: blue;"&gt;"/ows"&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;dispatcher&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;prop&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8:         &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;prop&lt;/span&gt; &lt;span style="color: red;"&gt;key&lt;/span&gt;=&lt;span style="color: blue;"&gt;"/ows/**"&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;dispatcher&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;prop&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9:         &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;prop&lt;/span&gt; &lt;span style="color: red;"&gt;key&lt;/span&gt;=&lt;span style="color: blue;"&gt;"/styles/**"&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;filePublisher&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;prop&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;10:         &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;prop&lt;/span&gt; &lt;span style="color: red;"&gt;key&lt;/span&gt;=&lt;span style="color: blue;"&gt;"/www/**"&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;filePublisher&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;prop&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;11:       &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;props&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;12:     &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;property&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;13:   &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;bean&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/pre&gt;When ows requests come, they will be captured and handled in handleRequestInternal() method of dispatcher.&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: &lt;span style="color: blue;"&gt;protected&lt;/span&gt; ModelAndView handleRequestInternal(HttpServletRequest httpRequest,&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2:         HttpServletResponse httpResponse) &lt;span style="color: blue;"&gt;throws&lt;/span&gt; Exception {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:     ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:     &lt;span style="color: green;"&gt;// handle OWS requests&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:     ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6: }&lt;/pre&gt;&lt;/pre&gt;Within the dispatcher, for each request parameter it loops through all registered kvp parsers and apply them when “service” and “version” match. After that dispatcher will try to find the correct service and operation among all those being registered in application context based on the value of request parameter “service”, “version” and “request”. Take my “ags-ows” service as an example, if a request comes in like :&lt;br /&gt;&lt;br /&gt;&lt;a href="http://localhost:8080/geoserver/ows?service=agsows&amp;amp;request=export&amp;amp;bbox=-115.8,30.4,-85.5,50.5&amp;amp;bboxSR=4326&amp;amp;layers=show:0,1,2&amp;amp;size=800,600&amp;amp;imageSR=4326&amp;amp;transparent=true"&gt;http://localhost:8080/geoserver/ows?service=agsows&amp;amp;request=export&amp;amp;bbox=-115.8,30.4,-85.5,50.5&amp;amp;bboxSR=4326&amp;amp;layers=show:0,1,2&amp;amp;size=800,600&amp;amp;imageSR=4326&amp;amp;transparent=true&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;the dispatcher will find the instance of ArcGISServerOwsService and dispatch request to its export method because of what I’ve registered below in application context:&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;bean&lt;/span&gt; &lt;span style="color: red;"&gt;id&lt;/span&gt;=&lt;span style="color: blue;"&gt;"ags-ows"&lt;/span&gt; &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2:     &lt;span style="color: red;"&gt;class&lt;/span&gt;=&lt;span style="color: blue;"&gt;"org.geoserver.ows.arcgisserver.ArcGISServerOWSService"&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;constructor&lt;/span&gt;-&lt;span style="color: red;"&gt;arg&lt;/span&gt; &lt;span style="color: red;"&gt;ref&lt;/span&gt;=&lt;span style="color: blue;"&gt;"geoServer"&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;constructor&lt;/span&gt;-arg&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4: &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;bean&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;   &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5: &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;bean&lt;/span&gt; &lt;span style="color: red;"&gt;id&lt;/span&gt;=&lt;span style="color: blue;"&gt;"ags-ows-1.0.0"&lt;/span&gt; &lt;span style="color: red;"&gt;class&lt;/span&gt;=&lt;span style="color: blue;"&gt;"org.geoserver.platform.Service"&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;constructor&lt;/span&gt;-&lt;span style="color: red;"&gt;arg&lt;/span&gt; &lt;span style="color: red;"&gt;index&lt;/span&gt;=&lt;span style="color: blue;"&gt;"0"&lt;/span&gt; &lt;span style="color: red;"&gt;value&lt;/span&gt;=&lt;span style="color: blue;"&gt;"agsows"&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;constructor&lt;/span&gt;-arg&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;constructor&lt;/span&gt;-&lt;span style="color: red;"&gt;arg&lt;/span&gt; &lt;span style="color: red;"&gt;index&lt;/span&gt;=&lt;span style="color: blue;"&gt;"1"&lt;/span&gt; &lt;span style="color: red;"&gt;ref&lt;/span&gt;=&lt;span style="color: blue;"&gt;"ags-ows"&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;constructor&lt;/span&gt;-arg&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;constructor&lt;/span&gt;-&lt;span style="color: red;"&gt;arg&lt;/span&gt; &lt;span style="color: red;"&gt;index&lt;/span&gt;=&lt;span style="color: blue;"&gt;"2"&lt;/span&gt; &lt;span style="color: red;"&gt;value&lt;/span&gt;=&lt;span style="color: blue;"&gt;"1.0.0"&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;constructor&lt;/span&gt;-arg&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;constructor&lt;/span&gt;-&lt;span style="color: red;"&gt;arg&lt;/span&gt; &lt;span style="color: red;"&gt;index&lt;/span&gt;=&lt;span style="color: blue;"&gt;"3"&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;10:         &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;list&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;            &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;11:             &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;value&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;Export&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;value&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;                            &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;12:         &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;list&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;13:     &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;constructor&lt;/span&gt;-arg&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;14: &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;bean&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/pre&gt;After dispatcher locate the appropriate service and operation, it is aware which method in which service class to dispatch request to (in this case it’s the export() method in ArcGISServerOWSService class), it also knows number of types of input parameters for that method. So it will create the request bean by using an appropriate kvp request reader, and finally calls the operation method with request bean. You will see following in ows dispatcher.&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2: &lt;span style="color: green;"&gt;//dispatch the operation&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3: Operation operation = dispatch(request, service);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4: &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5: &lt;span style="color: green;"&gt;//execute it&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6: Object result = execute(request, operation);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7: ...&lt;/pre&gt;&lt;/pre&gt;If you follow previous posts closely, you probably notice all necessary parts so far in this work flow have been covered such that the OWS dispatcher is able to find ArcGISServerOWSService class and call its export method&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: &lt;span style="color: green;"&gt;/**&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2:   * a sample request:  &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:   * &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:   * &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:   */&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6: &lt;span style="color: blue;"&gt;public&lt;/span&gt; AgsOwsExportResponse export(AgsOwsExportRequest request) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7:     &lt;span style="color: blue;"&gt;return&lt;/span&gt; &lt;span style="color: blue;"&gt;new&lt;/span&gt; AgsOwsExportResponse(&lt;span style="color: blue;"&gt;this&lt;/span&gt;.geoServer);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8: }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9: &lt;/pre&gt;&lt;/pre&gt;What happens next is the most import part in this “ags-ows” service sample. In next post of this series I will focus on extending org.geoserver.ows.Response class to produce map response for “ags-ows” services and sends back to clients.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4314202365991499343-7186171563439773901?l=augusttown.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://augusttown.blogspot.com/feeds/7186171563439773901/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://augusttown.blogspot.com/2010/01/extend-geoserver-with-customized-ows_09.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/7186171563439773901'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/7186171563439773901'/><link rel='alternate' type='text/html' href='http://augusttown.blogspot.com/2010/01/extend-geoserver-with-customized-ows_09.html' title='Extend GeoServer with customized OWS service :: part 4'/><author><name>Yingqi Tang</name><uri>http://www.blogger.com/profile/00422288991527433117</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/--JG8BCe4IgI/Tja7MbVf-uI/AAAAAAAAAYQ/nde6VIRt3aY/s220/august_and_me.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4314202365991499343.post-6759429198176718664</id><published>2010-01-08T09:22:00.001-08:00</published><updated>2010-01-09T19:30:53.121-08:00</updated><title type='text'>Extend GeoServer with customized OWS service :: part 3</title><content type='html'>In &lt;a href="http://augusttown.blogspot.com/2010/01/extend-geoserver-with-customized-ows_7162.html"&gt;previous post&lt;/a&gt; of this series, I created the application context for “ags-ows” service project and also register a java bean of type org.geoserver.platform.Service in it (a bean instance of ArcGISServerOWSService along with the service name ‘ags-ows’ and version ‘1.0.0’ are passed in into constructor) so that GeoServer’s ows dispatcher can be reused to redirect requests to “ags-ows” service. A set of KvpParsers were also created and registered in application context to parse request parameters. So in this post, I will move onto &lt;b&gt;AgsOwsExportRequest &lt;/b&gt;which is the request bean class for “ags-ows” service, and &lt;b&gt;AgsOwsExportRequestKvpReader&lt;/b&gt; (a subclass of KvpRequestReader) which is used to to create request bean from parsed objects of request parameters.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;u&gt;Request bean and KvpRequestReader&lt;/u&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;A request bean is basically a java object carrying all parsed parameter key-value pairs (as well as their getter and setter methods) of a specific service operation’s request. For example GeoServer’s WMS service has GetMap operation so which uses GetMapRequest (org.vfny.geoserver.wms.requests.GetMapRequest) bean, and same idea GetCapabilities operation has WMSCapabilitiesRequest bean. Now for “ags-ows” service, since “export” is the only operation declared in ArcGISServerOWSService, I will create one request bean class and call it &lt;b&gt;AgsOwsExportRequest&lt;/b&gt; (below is a sample implementation that omits getters and setters):&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;class&lt;/span&gt; AgsOwsExportRequest {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2:     &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:   &lt;span style="color: blue;"&gt;private&lt;/span&gt; LayerInfo[] layers;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:   &lt;span style="color: blue;"&gt;private&lt;/span&gt; Envelope bbox;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:   &lt;span style="color: blue;"&gt;private&lt;/span&gt; AgsOwsExportSize size;  &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6:   &lt;span style="color: blue;"&gt;private&lt;/span&gt; CoordinateReferenceSystem imageSR;  &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7:   &lt;span style="color: blue;"&gt;private&lt;/span&gt; CoordinateReferenceSystem bboxSR;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8:   &lt;span style="color: blue;"&gt;private&lt;/span&gt; &lt;span style="color: blue;"&gt;boolean&lt;/span&gt; transparent;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9:   &lt;span style="color: blue;"&gt;private&lt;/span&gt; Color bgColor = Color.WHITE;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;10:   &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;11:         &lt;span style="color: blue;"&gt;public&lt;/span&gt; AgsOwsExportRequest() {}&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;12:        &lt;span style="color: green;"&gt;/*&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;13:         * omitted getter and setter methods&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;14:         * you need to add getter and setter in your own code &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;15:         */&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;16: }&lt;/pre&gt;&lt;/pre&gt;you probably notice that each request bean parameter represents one request parameter of “ags-ows” service, and the type of the parameter matches the binding class of parameter’s kvp parser. (e.g. AgsOwsKvpLayersParser binds to List&amp;lt;LayerInfo&amp;gt;, AgsOwsKvpSizeParser binds to AgsOwsExportSize class etc.)&lt;br /&gt;&lt;br /&gt;Usually an OWS service’s operation logic takes its own request bean as input parameter (that’s why you saw AgsOwsExportRequest in previous post which I didn’t explain much then). Before the dispatcher redirects request the a service operation logic for further processing, it will create an instance of appropriate type of request bean and pass it in, and it’s actually a KvpRequestReader subclass’s job to create such request bean and populate it with parsed parameter objects from kvp parsers. So I created AgsOwsExportRequestKvpReader as below, which extends KvpRequestReader:&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;class&lt;/span&gt; AgsOwsExportRequestKvpReader &lt;span style="color: blue;"&gt;extends&lt;/span&gt; KvpRequestReader {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2: &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:   &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:   &lt;span style="color: blue;"&gt;public&lt;/span&gt; AgsOwsExportRequestKvpReader() {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:     &lt;span style="color: blue;"&gt;super&lt;/span&gt;(AgsOwsExportRequest.&lt;span style="color: blue;"&gt;class&lt;/span&gt;);    &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6:   }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7: &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8:   @Override&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9:   &lt;span style="color: blue;"&gt;public&lt;/span&gt; Object createRequest() &lt;span style="color: blue;"&gt;throws&lt;/span&gt; Exception {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;10:     AgsOwsExportRequest requestBean = &lt;span style="color: blue;"&gt;new&lt;/span&gt; AgsOwsExportRequest();    &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;11:     &lt;span style="color: blue;"&gt;return&lt;/span&gt; requestBean;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;12:         }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;13:   &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;14:   @Override&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;15:   &lt;span style="color: blue;"&gt;public&lt;/span&gt; Object read(Object request, Map kvp, Map rawKvp) &lt;span style="color: blue;"&gt;throws&lt;/span&gt; Exception {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;16:     AgsOwsExportRequest exportRequest = (AgsOwsExportRequest)&lt;span style="color: blue;"&gt;super&lt;/span&gt;.read(request, kvp, rawKvp);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;17:     &lt;span style="color: green;"&gt;/*&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;18:      * do any specific setting for AgsOwsExportRequest request bean instance - &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;19:      * - based on kvp and rawKvp &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;20:      */&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;21:     &lt;span style="color: green;"&gt;// TODO: here shall I set default value for certain parameters -&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;22:     &lt;span style="color: green;"&gt;// - if they are missing from kvp?    &lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;23:     &lt;span style="color: blue;"&gt;return&lt;/span&gt; exportRequest;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;24:   }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;25: }&lt;/pre&gt;&lt;/pre&gt;To extend KvpRequestReader, you basically override its createRequest() and read() method, and optionally set attribute “filter”. &lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;createRequest()&lt;/b&gt; creates and returns a new instance of appropriate request bean object. Default creatRequest() does that by creating the new instance reflectively using KvpRequestReader.requestBean class (you set that class in constructor), but if you need to set extra attribute of request bean object then you should probably override it. &lt;/li&gt;&lt;li&gt;&lt;b&gt;read()&lt;/b&gt; is usually called in ows dispather, and it takes a request bean instance created by createReques() as well as kvp (java.util.Map) parsed by KvpParsers and unparsed raw kvp (java.util.Map), and it is suppose to loop through each key-value pair of parsed kvp map and set the applicable value in request bean instance if there is a setter defined for the key. &lt;/li&gt;&lt;li&gt;“&lt;b&gt;filter&lt;/b&gt;” is an optional attribute (through setFilter(Set&amp;lt;String&amp;gt; filter)) you can set so that certain keys in kvp can be filtered out without setting the its value to request bean instance. &lt;/li&gt;&lt;/ul&gt;Finally as always, you need register your KvpRequestReader subclass in application context (but you don’t need do so for request bean class):&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1:   &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;bean&lt;/span&gt; &lt;span style="color: red;"&gt;id&lt;/span&gt;=&lt;span style="color: blue;"&gt;"agsOwsExportKvpRequestReader"&lt;/span&gt; &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2:     &lt;span style="color: red;"&gt;class&lt;/span&gt;=&lt;span style="color: blue;"&gt;"org.geoserver.ows.argisserver.kvp.AgsOwsExportRequestKvpReader"&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:   &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;bean&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/pre&gt;Now it’s time to validate what I’ve added in “ags-ows” service project in this post so far. Restart GeoServer in debug mode, and set a break point in org.geoserver.ows.Dispatcher’s &lt;b&gt;parseRequestKvp &lt;/b&gt;method (see highlighted line below), and send a “ags-ows” service request:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://localhost:8080/geoserver/ows/agsows?service=agsows&amp;amp;request=export&amp;amp;version=1.0.0&amp;amp;bbox=-124.731,24.956,-66.97,49.372&amp;amp;transparent=true&amp;amp;size=512,384&amp;amp;bboxSR=4326&amp;amp;imageSR=4326&amp;amp;layers=states" title="http://sazabi:9090/geoserver/ows/agsows?service=agsows&amp;amp;request=export&amp;amp;version=1.0.0&amp;amp;bbox=-124.731,24.956,-66.97,49.372&amp;amp;transparent=true&amp;amp;size=512,384&amp;amp;bboxSR=EPSG:4326&amp;amp;imageSR=4326&amp;amp;layers=states"&gt;http://localhost:8080/geoserver/ows/agsows?service=agsows&amp;amp;request=export&amp;amp;version=1.0.0&amp;amp;bbox=-124.731,24.956,-66.97,49.372&amp;amp;transparent=true&amp;amp;size=512,384&amp;amp;bboxSR=4326&amp;amp;imageSR=4326&amp;amp;layers=show:states&lt;/a&gt;&lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1:     Object parseRequestKVP(Class type, Request request)&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2:         &lt;span style="color: blue;"&gt;throws&lt;/span&gt; Exception {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:         KvpRequestReader kvpReader = findKvpRequestReader(type);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4: &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:         &lt;span style="color: blue;"&gt;if&lt;/span&gt; (kvpReader != &lt;span style="color: blue;"&gt;null&lt;/span&gt;) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6:             &lt;span style="color: green;"&gt;//check for http request awareness&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7:             &lt;span style="color: blue;"&gt;if&lt;/span&gt; (kvpReader &lt;span style="color: blue;"&gt;instanceof&lt;/span&gt; HttpServletRequestAware) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8:                 ((HttpServletRequestAware) kvpReader).setHttpRequest(request.httpRequest);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9:             }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;10: &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;11:             Object requestBean = kvpReader.createRequest();&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;12: &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;13:             &lt;span style="color: blue;"&gt;if&lt;/span&gt; (requestBean != &lt;span style="color: blue;"&gt;null&lt;/span&gt;) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;14:                 requestBean = kvpReader.read(requestBean, request.kvp, request.rawKvp);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;15:             }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;16: &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: yellow; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;17:             &lt;span style="color: blue;"&gt;return&lt;/span&gt; requestBean;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;18:         }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;19: &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;20:         &lt;span style="color: blue;"&gt;return&lt;/span&gt; &lt;span style="color: blue;"&gt;null&lt;/span&gt;;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;21:     }&lt;/pre&gt;&lt;/pre&gt;when it stops at the break point, check if variable “requestBean” is of class AgsOwsExportRequest, and also check if those attributes of AgsOwsExportRequest (e.g. “layers”, “bbox”, “imageSR” etc.) are populated with correct type of object values from kvp parsers. If so, it means AgsOwsExportRequest and AgsOwsExportRequestKvpReader are working as expected.&lt;br /&gt;&lt;br /&gt;By now enough logics and functions have been added into “ags-ows” service to enable it to parse the request from client,&amp;nbsp; in next post of this series, I will discuss more on the whole picture of how an ows request is processed by GeoServer OWS dispatcher, which is also a good half-way summary of what I've done so far.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4314202365991499343-6759429198176718664?l=augusttown.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://augusttown.blogspot.com/feeds/6759429198176718664/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://augusttown.blogspot.com/2010/01/extend-geoserver-with-customized-ows_08.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/6759429198176718664'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4314202365991499343/posts/default/6759429198176718664'/><link rel='alternate' type='text/html' href='http://augusttown.blogspot.com/2010/01/extend-geoserver-with-customized-ows_08.html' title='Extend GeoServer with customized OWS service :: part 3'/><author><name>Yingqi Tang</name><uri>http://www.blogger.com/profile/00422288991527433117</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/--JG8BCe4IgI/Tja7MbVf-uI/AAAAAAAAAYQ/nde6VIRt3aY/s220/august_and_me.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4314202365991499343.post-6458311735333384826</id><published>2010-01-07T09:56:00.001-08:00</published><updated>2010-01-07T17:25:06.594-08:00</updated><title type='text'>Extend GeoServer with customized OWS service :: part 2</title><content type='html'>In previous post of this series, I've created "ags-ows" project under group id "playground". It will be rebuilt when GeoServer is rebuilt and you can also debug it along with other core GeoServer source code. So start from this post I am going to add more elements in this project little by little.   &lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;Create application context (applicationContext.xml)&lt;/b&gt;&lt;/u&gt;&lt;br /&gt;&lt;br /&gt;The first thing to add for "ags-ows" service is to create application context which is configured through &lt;b&gt;applicationContext.xml&lt;/b&gt; file in your project's src folder (e.g. \trunk\src\playground\ows-arcgisserver\src\main\java). applicationContext.xml is very important because service url mapping, services and operations, KvpParser, KvpRequestReader and Response almost everything I will create later must be registered as Java beans in it so that they can be loaded when GeoServer starts.   &lt;br /&gt;For now, you can start with an empty applicationContext.xml in your ows-arcgisserver project's root source code folder (\src\main\java)   &lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: &lt;span style="color: blue;"&gt;&amp;lt;?&lt;/span&gt;xml version="1.0" encoding="UTF-8"&lt;span style="color: blue;"&gt;?&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2: &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;beans&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3: &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;beans&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/pre&gt;&lt;u&gt;&lt;b&gt;Reuse org.geoserver.ows.Dispatcher to dispatch the request&lt;/b&gt;&lt;/u&gt; &lt;br /&gt;&lt;br /&gt;Class &lt;b&gt;org.geoserver.ows.Dispatcher&lt;/b&gt; in GeoServer ows project is like a central hub which dispatches all ows service requests (e.g. WMS, WFS or WCS request) to appropriate OGC server implementation (wms, wfs or wcs GeoServer project) for further processing based a request's "service" and "version" parameters (e.g. if "service=WMS" then the request will be dispatched to wms implementation). If you look at the Spring application context in GeoServer "main" project you will find following servlet url mapping: &lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;bean&lt;/span&gt; &lt;span style="color: red;"&gt;id&lt;/span&gt;=&lt;span style="color: blue;"&gt;"dispatcherMapping"&lt;/span&gt; &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2:     &lt;span style="color: red;"&gt;class&lt;/span&gt;=&lt;span style="color: blue;"&gt;"org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:   &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;property&lt;/span&gt; &lt;span style="color: red;"&gt;name&lt;/span&gt;=&lt;span style="color: blue;"&gt;"alwaysUseFullPath"&lt;/span&gt; &lt;span style="color: red;"&gt;value&lt;/span&gt;=&lt;span style="color: blue;"&gt;"true"&lt;/span&gt;&lt;span style="color: blue;"&gt;/&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:   &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;property&lt;/span&gt; &lt;span style="color: red;"&gt;name&lt;/span&gt;=&lt;span style="color: blue;"&gt;"mappings"&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;props&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;prop&lt;/span&gt; &lt;span style="color: red;"&gt;key&lt;/span&gt;=&lt;span style="color: blue;"&gt;"/ows"&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;dispatcher&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;prop&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;prop&lt;/span&gt; &lt;span style="color: red;"&gt;key&lt;/span&gt;=&lt;span style="color: blue;"&gt;"/ows/**"&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;dispatcher&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;prop&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;prop&lt;/span&gt; &lt;span style="color: red;"&gt;key&lt;/span&gt;=&lt;span style="color: blue;"&gt;"/styles/**"&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;filePublisher&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;prop&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;prop&lt;/span&gt; &lt;span style="color: red;"&gt;key&lt;/span&gt;=&lt;span style="color: blue;"&gt;"/www/**"&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;filePublisher&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;prop&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;10:     &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;props&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;11:   &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;property&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;12: &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;bean&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/pre&gt;So all the requests matching pattern ".../geoserver/ows?..." or ".../geoserver/ows/*?..." (e.g. .../geoserver/ows/wms?) will be preprocessed and dispatched by ows dispatcher. This makes thing much easier for me because "ags-ows" service is suppose to be a fake OWS service that expects request like below: &lt;br /&gt;&lt;br /&gt;&lt;a href="http://localhost:8080/geoserver/ows/agsows?request=export&amp;amp;bbox=-115.8,30.4,-85.5,50.5&amp;amp;bboxSR=4326&amp;amp;layers=show:0,1,2&amp;amp;size=800,600&amp;amp;imageSR=4326&amp;amp;transparent=true"&gt;http://localhost:8080/geoserver/ows/agsows?request=export&amp;amp;bbox=-115.8,30.4,-85.5,50.5&amp;amp;bboxSR=4326&amp;amp;layers=show:0,1,2&amp;amp;size=800,600&amp;amp;imageSR=4326&amp;amp;transparent=true&lt;/a&gt; &lt;br /&gt;Or &lt;br /&gt;&lt;a href="http://localhost:8080/geoserver/ows?service=agsows&amp;amp;request=export&amp;amp;bbox=-115.8,30.4,-85.5,50.5&amp;amp;bboxSR=4326&amp;amp;layers=show:0,1,2&amp;amp;size=800,600&amp;amp;imageSR=4326&amp;amp;transparent=true"&gt;http://localhost:8080/geoserver/ows?service=agsows&amp;amp;request=export&amp;amp;bbox=-115.8,30.4,-85.5,50.5&amp;amp;bboxSR=4326&amp;amp;layers=show:0,1,2&amp;amp;size=800,600&amp;amp;imageSR=4326&amp;amp;transparent=true&lt;/a&gt; &lt;br /&gt;&lt;br /&gt;Basically I can just reuse the ows dispatcher without changing even one line of code. But now if you send the requests above, it won't work because the dispatcher does not know the existence of "ags-ows" service. Two things must be done before dispatcher correctly dispatches the request when it contains "service=agsows": &lt;br /&gt;&lt;br /&gt;1. Create a class representing "ags-ows" service which declares all the supported operations as its methods; (DefaultWebCoverageService111 class in wcs1_1 project is a good example of such class), in my case, I created one class called &lt;b&gt;ArcGISServerOWSService&lt;/b&gt;: &lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;class&lt;/span&gt; ArcGISServerOWSService {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2:     &lt;span style="color: blue;"&gt;public&lt;/span&gt; ArcGISServerOWSService(...) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:         &lt;span style="color: green;"&gt;//... &lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:     }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:     &lt;span style="color: blue;"&gt;public&lt;/span&gt; AgsOwsExportResponse export(AgsOwsExportRequest request) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6:     &lt;span style="color: green;"&gt;//...&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7:     }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8: }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9: &lt;/pre&gt;&lt;/pre&gt;For now don't worry about where class AgsOwsExportResponse and AgsOwsExportRequest come from, and other details which you can find out in attached sample code. &lt;br /&gt;&lt;br /&gt;2. Register "ags-ows" service class in application context as below: &lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: &lt;span style="color: blue;"&gt;&amp;lt;?&lt;/span&gt;xml version="1.0" encoding="UTF-8"&lt;span style="color: blue;"&gt;?&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2: &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;beans&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:   &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;bean&lt;/span&gt; &lt;span style="color: red;"&gt;id&lt;/span&gt;=&lt;span style="color: blue;"&gt;"ags-ows"&lt;/span&gt; &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:            &lt;span style="color: red;"&gt;class&lt;/span&gt;=&lt;span style="color: blue;"&gt;"org.geoserver.ows.arcgisserver.ArcGISServerOWSService"&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:           &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;constructor&lt;/span&gt;-&lt;span style="color: red;"&gt;arg&lt;/span&gt; &lt;span style="color: red;"&gt;ref&lt;/span&gt;=&lt;span style="color: blue;"&gt;"geoServer"&lt;/span&gt;&lt;span style="color: blue;"&gt;/&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6:   &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;bean&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7:   &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;bean&lt;/span&gt; &lt;span style="color: red;"&gt;id&lt;/span&gt;=&lt;span style="color: blue;"&gt;"ags-ows-1.0.0"&lt;/span&gt; &lt;span style="color: red;"&gt;class&lt;/span&gt;=&lt;span style="color: blue;"&gt;"org.geoserver.platform.Service"&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;constructor&lt;/span&gt;-&lt;span style="color: red;"&gt;arg&lt;/span&gt; &lt;span style="color: red;"&gt;index&lt;/span&gt;=&lt;span style="color: blue;"&gt;"0"&lt;/span&gt; &lt;span style="color: red;"&gt;value&lt;/span&gt;=&lt;span style="color: blue;"&gt;"agsows"&lt;/span&gt;&lt;span style="color: blue;"&gt;/&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;constructor&lt;/span&gt;-&lt;span style="color: red;"&gt;arg&lt;/span&gt; &lt;span style="color: red;"&gt;index&lt;/span&gt;=&lt;span style="color: blue;"&gt;"1"&lt;/span&gt; &lt;span style="color: red;"&gt;ref&lt;/span&gt;=&lt;span style="color: blue;"&gt;"ags-ows"&lt;/span&gt;&lt;span style="color: blue;"&gt;/&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;10:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;constructor&lt;/span&gt;-&lt;span style="color: red;"&gt;arg&lt;/span&gt; &lt;span style="color: red;"&gt;index&lt;/span&gt;=&lt;span style="color: blue;"&gt;"2"&lt;/span&gt; &lt;span style="color: red;"&gt;value&lt;/span&gt;=&lt;span style="color: blue;"&gt;"1.0.0"&lt;/span&gt;&lt;span style="color: blue;"&gt;/&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;11:     &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;constructor&lt;/span&gt;-&lt;span style="color: red;"&gt;arg&lt;/span&gt; &lt;span style="color: red;"&gt;index&lt;/span&gt;=&lt;span style="color: blue;"&gt;"3"&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;12:       &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;list&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;13:         &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;value&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;Export&lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;value&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;14:       &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;list&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;15:     &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;constructor&lt;/span&gt;-arg&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;16:   &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;bean&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;17: &lt;span style="color: blue;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon;"&gt;beans&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/pre&gt;As you can see, you first need to register ArcGISServerOWSService (geoserver bean is input parameter of the constructor) as a bean in application context. But what's more important is to add another bean in type org.geoserver.platform.Service, in which you must give "service" (agsows), the reference to the ArcGISServerOWSService bean, "version" (1.0.0), and list of supported operations (Export) as constructor input parameters. Once you save the application context and restart GeoServer, OWS Dispatcher will recognize the request with "service=agsows" and redirect to ArcGISServerOWSService implementation. &lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;Extend org.geoserver.ows.KvpParser to parse request parameter&lt;/b&gt;&lt;/u&gt; &lt;br /&gt;&lt;br /&gt;A subclass of org.geoserver.ows.KvpParser is used by ows dispatcher to parse a particular request parameter value in HTTP Get request. The reason to parse parameter value is that those values are coming in as String (could be UTF-8 encoded or other) but before they can be correctly recognized and used by GeoServer they must be converted to appropriate type of Java object or primitive types. For example, WMS request parameter 'bbox' needs to be converted as an JTS Envelope object, or WMS request parameter 'bgcolor' must be converted into java.awt.Color object, and a subclass of KvpParser does exactly that job. &lt;br /&gt;&lt;br /&gt;To extend KvpParser, you need to give a key which is the target request parameter it parses and a Class type in KvpParser subclass's constructor. The Class type is the type into which string request parameter value will be converted. &lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;abstract&lt;/span&gt; &lt;span style="color: blue;"&gt;class&lt;/span&gt; KvpParser {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2:     ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:    &lt;span style="color: green;"&gt;/**&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:     * The key.&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:     */&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6:     String key;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7: &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8:    &lt;span style="color: green;"&gt;/**&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9:     * The class of parsed objects.&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;10:     */&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;11:     Class binding;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;12: &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;13:     &lt;span style="color: blue;"&gt;public&lt;/span&gt; KvpParser(String key, Class binding) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;14:         &lt;span style="color: blue;"&gt;this&lt;/span&gt;.key = key;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;15:         &lt;span style="color: blue;"&gt;this&lt;/span&gt;.binding = binding;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;16:     }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;17:     ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;18: }&lt;/pre&gt;&lt;/pre&gt;You also need to override method "parse(String value)" that contains the actual logic to do the converting; for example the parse method in a WMS bbox kvp parser will create and return an JTS Envelope from a string in pattern "-minx, miny, maxx, maxy". &lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2: &lt;span style="color: green;"&gt;/**&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3: * Parses the string representation into the object representation.&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4: *&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5: * @param value The string value.&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6: *&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7: * @return The parsed object, or null if it could not be parsed.&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8: *&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9: * @throws Exception In the event of an unsuccesful parse.&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;10: */&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;11: &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;abstract&lt;/span&gt; Object parse(String value) &lt;span style="color: blue;"&gt;throws&lt;/span&gt; Exception;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;12: ...&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;13: &lt;/pre&gt;&lt;/pre&gt;In "ags-ows" service, one kvp parser is needed for each request parameter. But I don't have to actually create 5 different KvpParser subclasses because I can reuse some existing ones, for example org.geoserver.ows.kvp.BooleanKvpParser can be used for parameter "transparent", and org.geoserver.wfs.kvp.BBoxKvpParser can be used for parameter "bbox". For the rest, I am going to create my own. First I need a new "AgsOwsKvpLayersParser" to convert comma separated layer list into an array of GeoServer LayerInfo. Below is a sample implmentation: &lt;br /&gt;&lt;pre style="background-color: #fbfbfb; border: 1px solid rgb(206, 206, 206); min-height: 40px; overflow: auto; padding: 5px; width: 640px;"&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;1: &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;class&lt;/span&gt; AgsOwsKvpLayersParser &lt;span style="color: blue;"&gt;extends&lt;/span&gt; KvpParser &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;2:   &lt;span style="color: blue;"&gt;implements&lt;/span&gt; IArcGISServerOWSServiceLogger {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;3:   &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;4:   &lt;span style="color: blue;"&gt;private&lt;/span&gt; GeoServer geoServer;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;5:   &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;6:   &lt;span style="color: blue;"&gt;public&lt;/span&gt; AgsOwsKvpLayersParser(String service, String version, GeoServer geoServer) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;7:     &lt;span style="color: blue;"&gt;super&lt;/span&gt;("&lt;span style="color: darkred;"&gt;layers&lt;/span&gt;", List.&lt;span style="color: blue;"&gt;class&lt;/span&gt;);  &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;8:     setService(service);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;9:     setVersion(&lt;span style="color: blue;"&gt;new&lt;/span&gt; Version(version));&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;10:     &lt;span style="color: blue;"&gt;this&lt;/span&gt;.geoServer = geoServer;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;11:   }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;12: &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;13:   @Override&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;14:   &lt;span style="color: blue;"&gt;public&lt;/span&gt; Object parse(String value) &lt;span style="color: blue;"&gt;throws&lt;/span&gt; Exception {    &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;15:     AGSOWSLOGGER.info("&lt;span style="color: darkred;"&gt;layers parsed by AgsOwsKvpLayersParser&lt;/span&gt;");                &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;16:     &lt;span style="color: green;"&gt;/*&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;17:      * Syntax: [show | hide | include | exclude]:layerId1,layerId2&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;18:      */        &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;19:     Tokenizer outter_delimeter = &lt;span style="color: blue;"&gt;new&lt;/span&gt; Tokenizer("&lt;span style="color: darkred;"&gt;:&lt;/span&gt;");&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;20:     List unparsed = KvpUtils.readFlat(value, outter_delimeter);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;21:     &lt;span style="color: blue;"&gt;boolean&lt;/span&gt; isInclude = &lt;span style="color: blue;"&gt;true&lt;/span&gt;;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;22:     &lt;span style="color: blue;"&gt;if&lt;/span&gt;(unparsed.size() == 2) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;23:       &lt;span style="color: blue;"&gt;if&lt;/span&gt;(((String)unparsed.get(0)).equalsIgnoreCase("&lt;span style="color: darkred;"&gt;show&lt;/span&gt;") == &lt;span style="color: blue;"&gt;true&lt;/span&gt;) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;24:         isInclude = &lt;span style="color: blue;"&gt;true&lt;/span&gt;;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;25:       } &lt;span style="color: blue;"&gt;else&lt;/span&gt; &lt;span style="color: blue;"&gt;if&lt;/span&gt;(((String)unparsed.get(0)).equalsIgnoreCase("&lt;span style="color: darkred;"&gt;hide&lt;/span&gt;") == &lt;span style="color: blue;"&gt;true&lt;/span&gt;) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;26:         isInclude = &lt;span style="color: blue;"&gt;false&lt;/span&gt;;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;27:       } &lt;span style="color: blue;"&gt;else&lt;/span&gt; &lt;span style="color: blue;"&gt;if&lt;/span&gt;(((String)unparsed.get(0)).equalsIgnoreCase("&lt;span style="color: darkred;"&gt;include&lt;/span&gt;") == &lt;span style="color: blue;"&gt;true&lt;/span&gt;) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;28:         &lt;span style="color: green;"&gt;// TODO: implement later&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;29:       } &lt;span style="color: blue;"&gt;else&lt;/span&gt; &lt;span style="color: blue;"&gt;if&lt;/span&gt;(((String)unparsed.get(0)).equalsIgnoreCase("&lt;span style="color: darkred;"&gt;exclude&lt;/span&gt;") == &lt;span style="color: blue;"&gt;true&lt;/span&gt;) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;30:         &lt;span style="color: green;"&gt;// TODO: implement later&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;31:       }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;32:     }    &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;33:     List&amp;lt;LayerInfo&amp;gt; layers = &lt;span style="color: blue;"&gt;null&lt;/span&gt;;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;34:     List requestedLayers = KvpUtils.readFlat((String)unparsed.get(1), KvpUtils.INNER_DELIMETER);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;35:     &lt;span style="color: blue;"&gt;if&lt;/span&gt;(isInclude == &lt;span style="color: blue;"&gt;true&lt;/span&gt;) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;36:       layers = &lt;span style="color: blue;"&gt;new&lt;/span&gt; ArrayList&amp;lt;LayerInfo&amp;gt;();&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;37:     } &lt;span style="color: blue;"&gt;else&lt;/span&gt; {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;38:       layers = &lt;span style="color: blue;"&gt;this&lt;/span&gt;.geoServer.getCatalog().getLayers();&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;39:     }        &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;40:     &lt;span style="color: blue;"&gt;for&lt;/span&gt;(&lt;span style="color: blue;"&gt;int&lt;/span&gt; i=0; i&amp;lt;requestedLayers.size(); i++) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;41:       String layerName = (String)requestedLayers.get(i);      &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;42:       &lt;span style="color: blue;"&gt;if&lt;/span&gt;(layerName != &lt;span style="color: blue;"&gt;null&lt;/span&gt;) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;43:         LayerInfo layerInfo = &lt;span style="color: blue;"&gt;this&lt;/span&gt;.geoServer.getCatalog().getLayerByName(layerName);    &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;44:         &lt;span style="color: blue;"&gt;if&lt;/span&gt;(layerInfo != &lt;span style="color: blue;"&gt;null&lt;/span&gt;) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;45:           &lt;span style="color: blue;"&gt;if&lt;/span&gt;(isInclude == &lt;span style="color: blue;"&gt;true&lt;/span&gt;) {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;46:             &lt;span style="color: green;"&gt;// include layers being requested&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;47:             layers.add(layerInfo);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;48:           } &lt;span style="color: blue;"&gt;else&lt;/span&gt; {&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;49:             &lt;span style="color: green;"&gt;// exclude layers being requested&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;50:             layers.remove(layerInfo);&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;51:           }          &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;52:         }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;53:       }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;54:     }    &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;55:     &lt;span style="color: blue;"&gt;return&lt;/span&gt; layers;    &lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: white; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;56:   }&lt;br /&gt;&lt;/pre&gt;&lt;pre style="background-color: #e2e2e2; font-family: consolas,'Courier New',courier,monospace; font-size: 12px; margin: 0em; width: 100%;"&gt;57: }&lt;/pre&gt;&lt;/pre&gt;Second I need another kvp parser to parse parameter "size" which is in syntax "width,height". Since there is no such "size" class in JTS or GeoTools library I will create a simple class called "AgsOwsExportSize" to hold the width and height of a map and have kvp parser return an instance of that class. Below is a sample implementation of "AgsOwsKvp
