The firehose of information known as the FOSS4G conference is over. I had a great time. The best part was just getting to meet and trade ideas with so many people that I only knew via twitter or email lists.
Highlights of the conference:
I recently answered an email about the state of geospatial web apps for mobile devices and what his options were. Below are the questions and answers I gave. This is a space that is rapidly changing and further developments are constantly occuring. While this info might be current now (June 2011) it might be grossly out of date within a few months.
Is there any mobile platform that can display maps served from a web service, such as Geoserver ?
Short Answer: Yes, lots.
Long Answer:
OpenLayers 2.11
OpenLayers hosts a mobile optimized minimal build and you can also make your own. A recent code sprint added significant support for touch events, the geolocation API and several mobile enabled controls. OpenLayers can be used within a mobile app framework or on its own. You can see the mobile examples at http://m.openlayers.org
Google Maps (Bing Maps also, I guess)
Google Maps has a mobile optimized javascript api for displaying Google Maps background and interactive data layers from your own servers or theirs.
This is actually a really good option that works for a wide variety of platforms. However, if it is used in a private or commercial site (ie requires payment, logon or used only behind corporate firewall) then you have to pay between $5,000 - 10,000 USD/year
Update: Added Cloudmade's Leaflet (June 20, 2011)
Leaflet
A javascript mapping library that focuses on "A" grade browsers (desktop & mobile). An alternative to OpenLayers. This is newer than the next 2 javascript mapping libraries, but is being developed by Cloudmade and should have good support and forward momentum. Supports regular tiled base maps, WMS base maps, GeoJSON vector data and static image overlays. If you must have WMS base map support then this is probably the most viable OpenLayers alternative.
Tile5
A javascript mapping library that uses HTML5 technologies and doesn't attempt to support older browsers. It is designed with a mobile-first focus and integrates well with JQuery Mobile and Sencha Touch, but can also be used well on its own. It is an alternative to OpenLayers and uses a completely different code base. It supports rendering tiled map backgrounds along with vector data in GeoJSON format. The library also includes a few map controls as well.
Polymaps
Polymaps is similar to Tile5 but has more intentional support for desktop and uses SVG as the primary rendering method rather than Canvas as Tile5 does. In other words, this is another alternative to OpenLayers and can also be displayed by itself or integrated into a mobile app framework like JQueryMobile or Sencha Touch. OpenLayers, Tile5 and Polymaps all use very different javascript code bases and can't broadly share any code.
Sencha Touch with 1 of the above mapping libraries
http://www.sencha.com/products/touch/
There are examples and some demo sites using Sencha Touch with the newly touch enabled version of OpenLayers.
I could not get any of these examples to work well on my Android 2.1, 2.2 (Froyo), or 2.3 (Gingerbread) devices, but they do seem to work well on iPhones / iPads and Android 3.x (Honeycomb) Tablets.
JQuery Mobile with 1 of the above mapping libraries
The examples and demos using JQueryMobile and the touch enabled version of OpenLayers seem to work well across a wider variety of devices.
I recently developed an application using that combination (currently backed by Mapserver and changing to Geoserver as soon as possible for the better output format support and printing module).
If I was going to choose a mobile framework for using OpenLayers, then this the best current choice.
GeoExt
There is an active project supported by the Swiss government to create a Sencha Touch version of GeoExt that includes the most important part of that framework written for Sencha's mobile framework which is not directly compatible with ExtJS 3 but shares common elements with the newly released ExtJS 4. The general consensus is that GeoExt 2 will be based on Sencha Touch & Sencha ExtJS 4 and thus share a common code base for mobile and desktop apps. The initial Sencha Touch based version of Mobile GeoExt should be released, tested and ready for use in the late summer.
ExtJS 3 & GeoExt 1 don't work particularly well on mobile devices.
Does JQuery Mobile also support a host of different phones (perhaps through phonegap) ?
Yes, JQuery Mobile is fully compatible with PhoneGap and you can generate iPhone, Blackberry, Android, Palm, and Symbian native apps (support for Windows Mobile, MeeGo & Bada apps is planned in the near future)
Can we capture photo’s, video and link it to the geo-locater and compass.
You can use the geolocation and orientation features in a mobile browser app. You can have people upload or link pictures/video already on the phone (you can, with some extra permission dialogs, get the camera app to open from a web app, if you really want to). However to truly interact live with a phone's camera you must create a native app
There are numerous use cases in which you might need to place markers on a map and then update their position & direction at regular intervals. OpenLayers provides good methods for doing this. I needed to track vehicle movements received at regular intervals via GeoJSON, but for some reason things were not quite behaving as I expected. So I searched the OpenLayers mailing lists to find an example of someone else using an external graphic image and automatic style based rotation.
I found this post (http://n2.nabble.com/externalGraphic-rotation-in-a-vector-layer-td1828899.html) linked to this example (http://www.otg-nc.com/csc/index1.html).
However, that example uses techniques & conventions from somewhere between OpenLayers 2.6 & 2.7. It also merely removes the marker & then adds it back in the new position. That technique is TERRIBLY leaky and quickly eats up system memory. It also doesn’t work in IE 7.
I updated the samples to OpenLayers 2.8 techniques & conventions & changed it so that it works in at least IE7, FF3, & Chrome 2.
Examples:
No memory leaks in IE7, slight memory leaks (4 - 60kb per update interval) in FF3 & Chrome 2
Big memory leaks in IE7, slightly smaller memory leaks in FF3 & Chrome 2. Leaks between 250kb - 1.25Mb per update interval depending on browser
This example doesn't really work right & looks much worse than the first example. Not sure if it is my code or OpenLayers's HTML5 Canvas Renderer
Conclusions:
I like using ArcGIS Table Based Image Catalogs when working with large numbers of rasters in ArcMap. The old style table based Image Catalogs work for clients that still have ArcGIS 8.x or even ArcView 3.x. While a TileCache/Mapserver WMS image server is faster, it is sometimes not a real option for certain clients & situations.
However, lately I've been running into a number of situations in which the Image Catalog creator scripts that I've always used : USGS Image Catalog Builder or This One aren't working. On Vista, the user extensions won't even register and thus are of no use. I've also had troubles with large numbers of the newest MrSID type imagery giving "Automation Error" errors & the script stops working.
So I needed another way to create image catalogs that was easy and wouldn't require me installing additional software on clients' computers, etc.. While, I could have used Python or some other script, I wanted something that people without any scripting or coding experience could also do.
Software Used:
Steps:
gdaltindex naip_05_index.shp 2005_NAIP_NC\*.sid
ogr2ogr -f BNA naip05index.txt naip_05_index.shp
"2005_NAIP_NC\e2997_01_1.sid","",5
596140.0000000000,3319570.0000000000
602854.0000000000,3319570.0000000000
602854.0000000000,3311970.0000000000
596140.0000000000,3311970.0000000000
596140.0000000000,3319570.0000000000
IMAGE,A,B,C,D,XMAX,YMAX,E,F,XMIN,YMIN,G,H
THAT'S IT!
Now you have a CSV based text file table that you can use as an Image Catalog.
The only additional step you may need to take is to convert it to a DBF file to work properly on older versions of ArcGIS or ArcView. This can easily be accomplished in the ArcCatalog, ArcToolbox or by loading the table into a map and exporting it to a DBF. Alternatively you can just use Excel, Open Office, or DBF Explorer to convert it as well.
It actually took me much longer to write this post then it did for me to create several image catalogs this way.
handleJSON({"Observations":[{"Station":{"number":"1","lat":"29.5762","lon":"-98.7041","total_sp":"9","Species":[{"common":"Mourning Dove","number":"1","code":"MODO","scientific":"Zenaida macroura"},{"common":"Eastern Phoebe","number":"1","code":"EAPH","scientific":"Sayornis phoebe"},{"common":"Carolina Chickadee","number":"1","code":"CACH","scientific":"Poecile carolinensis"},{"common":"Black-crested Titmouse","number":"3","code":"BCTI","scientific":"Baeolophus atricristatus"},{"common":"Carolina Wren","number":"1","code":"CARW","scientific":"Thryothorus ludovicianus"},{"common":"Bewick's Wren","number":"2","code":"BEWR","scientific":"Thryomanes bewickii"},{"common":"Rufous-crowned Sparrow","number":"1","code":"RCSP","scientific":"Aimophila ruficeps"},{"common":"Northern Cardinal","number":"3","code":"NOCA","scientific":"Cardinalis cardinalis"},{"common":"Brown-headed Cowbird","number":"1","code":"BHCO","scientific":"Molothrus ater"}]}},{"Station":{"number":"2","lat":"29.574","lon":"-98.7036","total_sp":"5","Species":[{"common":"Black-crested Titmouse","number":"1","code":"BCTI","scientific":"Baeolophus atricristatus"},{"common":"Carolina Wren","number":"1","code":"CARW","scientific":"Thryothorus ludovicianus"},{"common":"Ruby-crowned Kinglet","number":"1","code":"RCKI","scientific":"Regulus calendula"},{"common":"Northern Cardinal","number":"1","code":"NOCA","scientific":"Cardinalis cardinalis"},{"common":"Brown-headed Cowbird","number":"1","code":"BHCO","scientific":"Molothrus ater"}]}}]});It is not very human readable, but it is highly machine readable. Below is an extremely simple example of handling JSON and doing something with it.
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <title>Simple JSON Handling</title> <script type="text/javascript" charset="utf-8"> function showCoords(jsd){ var lat = jsd.Observations[0].Station.lat; var lon = jsd.Observations[0].Station.lon; var msg = 'Lat:' + lat + ', Lon:' + lon; alert(msg); } </script> </head> <body> <script src=http://www.plateauwildlife.com/bbc-mgmt/getstations.php?action=getdata&cid=2&year=2006&func=showCoords type="text/javascript" charset="utf-8"></script> </body> </html>See It In Action The above example is not particulalry usefull for anything other than demostration purposes. We want to actually DO something with our JSON to move us closer to creating the actual mashup. The number thing which I intially strugled with when using JSON & callback functions was that you MUST define the callback function BEFORE your dynamic or static script tag. Google Maps, Google Earth, Yahoo Maps, and Virtual Earth all take HTML for the contents of the info window when you rollover or click on a point. ArcWeb Explorer (AWX), however doesn't take HTML as info window content. AWX does take styled text, videos, picture, audio, & swf for info window content. To this end, if you want to embed rich non-HTML content, AWX allows for some extremelly interesting content to be blended together and presented with great ease. The documentation for text styling is lacking, so creating simple content is actually more dificult in this platform than the others. I've created a javascript file which we can reference in any of the HTML docs that actually embed the mashup. This file contains
//A global variable to assign the parsed JSON to
var jsobj;
//Main Callback handler.
//simple assignment to a global variable allows me to reuse and pass
//around the object without any server trips
function handleJSON(reply){
jsobj=reply;
}
function buildHTML(Observations){
var info_win = new Array();
// Build a table element with Station number & total species observed
for(var i=0;i<Observations.length;i++){
str = '<table border="1"><tbody><tr class="station">';
str += '<td colspan="2">Station '+ Observations[i].Station.number + '</td></tr>';
str += '<tr class="sta_total"><td colspan="2">Total Species - ' + Observations[i].Station.total_sp + '</td></tr>';
str += '<tr class="obs_header"><td>Species</td><td>Number</td></tr>';
var details = "";
var arr = new Array();
// assign each Species array to a local variable to reduce typing & increase readibility
//build an string of <tr> elements containing the details of species observed
arr = Observations[i].Station.Species;
for (var y=0;y<arr.length;y++){
details+='<tr class="obs_detail"><td><a href="http://www.google.com/search?q=%22';
details += arr[y].scientific.replace(/\s/,"+");
details += '%22">' + arr[y].common + ' (' + arr[y].code + ')</a></td>';
details += '<td>' + arr[y].number + '</td></tr>';
}
str += details;
str += '</table></tr></tbody></table>';
//add table element to array of table element html strings
info_win[i]=str;
}
return info_win;
}
function buildAWXtxt(Observations){
var info_win = new Array();
// Build a formated text list for each Station number & total species observed
for(var i=0;i<Observations.length;i++){
str = 'Station ' + Observations[i].Station.number + '\n';
str += 'Total Species - ' + Observations[i].Station.total_sp + '\n';
str += 'Species Number';
var details = new Array();
// assign each Species array to a local variable to reduce typing & increase readibility
//build an array formatted text data elements containing the details of species observed
//using this convention, we can assign a url property to each species line
//through .data{elements[]} in the properties for each marker
arr = Observations[i].Station.Species;
for (var y=0;y<arr.length;y++){
details[y] = arr[y].common + ' (' + arr[y].code + ') - ' + arr[y].number + '\n';
}
var obs_info = new Array([str,details]);
//
//add table element to array of table element html strings
info_win[i]=obs_info;
}
return info_win;
}
Previous Parts
1. Emit JSON
Next Parts
3. Arcweb Explorer Mashup
4. Yahoo Maps Mashup
5. Virtual Earth Mashup
6. Google Maps Mashup
I needed a way to run SQL queries on structured text files & Excel files.
Download links available at end of post
For the last few months, I have been toiling away in the highly arcane world of fixed width text files. Very large ones at that (250-500mb). These come from text file data dumps of appraisal district databases. I need only a subset of these records. From there they go into Excel where they are cleaned up on a nearly line by line basis that can't be handled consistently by RegEx since data is recorded so differently by each county. From there I want to summarize that data on a single household basis. I could go in Excel & do:
However, this is rather time consuming since different data is provided by in different order and level of detail for each county. Also, for rolls with more than 25k records and over 100 fields these Excel formulas can take ~30min to complete. You then need to copy & paste values or you'll have to wait a long time anytime a recalc is performed. Needed a programmatic way to deal with this data and to minimize the human time to just records which required actual human eyes to modify.
Admittedly, creating an OleDb connection, command, reader & even datatable using ADO.Net methods is a pretty simple thing.
Dim connect As New OleDb.OleDbConnection
Dim strConnect As String = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\MyExcel.xls;Extended Properties=""Excel 8.0;HDR=Yes;IMEX=1"""
connect.ConnectionString = strConnect
Dim cmd As New OleDb.OleDbCommand("SELECT * FROM [Sheet1$] WHERE City='Austin' AND LandValue > 30000", connect)
Dim reader As OleDb.OleDbDataReader = cmd.ExecuteReader()
Dim dt As New DataTable
dt.Load(reader)
But I wanted something even simpler & significantly more flexible than that. I wanted an object that could handle any potential Jet datasource, field list, target table, and SQL Select statement. I wanted something that could be reused in a variety of different applications and not require any hard coding of these parameters or make me implement different ways to do this in each application. I needed a good object oriented design with high reusability. To this end I created the JetDbConnect.dll
Structure
Properties
Methods
Usage
Examples:
Get an OleDbReader for all records in an Excel file from a named sheet
dim jetdb as new JetDbConnect("C:\myfiles\ExcelFile.xls", "Hays County")
dim reader as OleDbReader = jetdb.ReaderGetAll()
-or-
dim jetdb as new JetDbConnect
dim reader as OleDbReader = jetdb.ReaderGetAll("C:\myfiles\ExcelFile.xls", "Hays County")
note: the class automatically adds the required decorations so that Hays County becomes [Hays County$] as required for SQL statements on an Excel datasource
Get a DataTable with select records & fields from delimited text file
dim jetdb as new JetDbConnect("C:\myfiles\some text file.txt")
dim dt as DataTable = jetdb.RecordsGetSql("zipcode='78258' AND land_value>10000","owner_name, zipcode, land_value")
note: I did not need to ever specify the TargetTable attribute since it is automatically set to "some text file.txt" and the Data Source is actually set to "C:\myfiles"
Get a list of available Sheets and Ranges from an Excel file without the need for any Office libaries or applications
dim jetdb as new JetDbConnect("C:\myfiles\ExcelFile.xls")
dim tables as List(of string) = jetdb.TablesList
Downloads
Day 3 Thurs Mar 22
I'm finally getting a chance to blog about this now. Steve, I, and nearly 2 dozen other attendees got totally screwed by the storms in Phoenix. However, at least we didn't have to sit on the tarmack for nearly 3 hours like the other group waiting at the same gate. Though, by the time I got home 25 hours after leaveing the conference, sleeping in the Phoenix airport and eating only pretzals and snack mix; that wait seemed pretty trivial. I hope everyone else also eventually got home safely.
General Thoughts
This was a short day. Only 2 sessions and then the lunch & closing session. It seemed that about 25-30% of the folks had already left and a number of the ESRI devs had left as well. The sessions seemed a bit less technical that those on Wensday.
Sessions Attended
Building & Using ArcGIS Server Map Caches (Best Practices)
The MapCache is a tile cache created by rasterization & tile cutting of the data you have presented in your MXD. The main take home points of this talk were:
Creating Windows & Web Apps with WPF
This was not at all about GIS. It was strictly on WPF.
WPF is very powerful & can provide you with a lot of UI design patterns.
Security & Permissions can be tightly controlled. Each app runs in a security sandbox. App origin, app settings, and client settings all interact to chose the most restrictive security level for the app.
You can do stuff with this technology TODAY. But, it's real power won't be realized until Visual Studio ORCAS is released and Vista or .Net 3 runtimes are more widely accepted. The 1 code for web & desktop paradigm is only for IE6+ with .Net 3 runtime right now. Must use WPF/E for other browser support.
Closing Session
Open Q&A after lunch. No really hard questions were asked, but Steve & other continued to point out that the extra cost of putting the ADF on a separate server is just not reasonable, and that the overall cost of ArcGIS Server is too high.
Technologies Used: PHP 5, MySQL 4.1.2, JavaScript, JSON
Software Used: PHP Designer, Aptana, Firebug, Firefox
While I was not particularly impressed with the ArcWeb Explorer & ArcWeb Services session on Tuesday, it did inspire me to want to do a mashup using my own data sources. I also wanted to compare the mashup creation process & end results across multiple possible mashup platforms. I decided to use JSON since all the mashup API's are in Javascript and it is an output format that both Google & Yahoo web services provide. I am already using JSON with the data entry interface for our Breeding Bird Census data that Plateau Land & Wildlife collects each spring.
JSON (or JavaScript Object Notation) is a well described data exchange format that is rapidly gaining favor over XML for web service data transport as it is less verbose and automatically parsed by Javascript.
While I had already created a JSON object model for use with our survey management system, it was structured in a totally different way than what I really wanted for my mashup. I also wanted to demonstrate doing this from scratch.
Step 1 - Design your JSON Object (i.e. Class Structure)
You want to put some thought into designing your class structure that you will emit. JSON can represent arrays, classes, and arrays of classes. The Good news is that JSON is VERY flexible, The Bad news is that JSON is VERY flexible. A general rule to follow in determining where you should use an array or class is that indexed lists = arrays, anything else is a class, or a class property (key:value pair). I just used the high tech method of pen & paper to design my JSON object, but you could also use any XML schema generation tool, VS2005 Class Designer, VIsio, etc.
My desired end product is a map showing the location & number of each point count station, with different symbols if it has been surveyed or not. A mouse over or click event will bring up an html table of with information about species observed and provide a Google search link for the bird based on it's scientific name. The common name usually gets you all kinds of results whereas the scientific name gets you good reference results.
Thus I need Station number, lat/lon for the station, total observed, number of each species observed and scientific name for each species.
JSON Object
{"Observations":[{"Station":
{"number":"9", "lat":"30.9999",
"lon":"-98.9999","total_species":"9",
{"Species":
[{"common":"American Robin",
"number":"9", "code":"AMRO",
"scientific":"Turdus migratorius "},
{species cont....}
] } }
]
}
Or in other words:
Array Observations contains Station classes with Properties
Step 2 - Create your JSON Object (i.e. Class Structure)
There are a whole bunch of ways to do this and really depends on what language your are programing in on your server side code. It basically boils down to either populating a class and then parse it to JSON through a method or class in your language, or directly writing the JSON string from some data source.
I chose the later for this project because my PHP install didn't have a built in method for this. But rolling your own is pretty easy.
function getData($cid,$myYear,$func) {//cid = client id, myYear=year of survey, func=callback function to handle response
//Get a count of unique species occurring on the property for each point survey station, even those which have no recorded observations
$q_species="SELECT DISTINCT st.sta_num as station, COUNT(species) as species, st.lat, st.lon
FROM Stations as st LEFT JOIN (Surveys as su) USING (station_id)
WHERE (st.client_id=$cid AND (su.client_id=$cid OR su.client_id IS NULL) AND su.year=$myYear)
GROUP BY station ORDER BY station;";
//Get the details of species observed at each station
$q_obs="SELECT su.species, su.number, sp.code, sp.scientific, st.sta_num as station
FROM Surveys as su, Species as sp, Stations as st
WHERE su.client_id=$cid AND su.year=$myYear AND su.species=sp.common
AND su.station_id=st.station_id
ORDER BY station, sp.species_id";
//Create the results sets
$resSpecies = mysql_query($q_species);
$resObs = mysql_query($q_obs);
2. Create the JSON String
//Loop through each Station & create the JSON string for that Station & it’s data
$myJSON= $func.'({"Observations":[';
While ($c=mysql_fetch_array($resSpecies)){
$myJSON.= '{"Station":{"number":"'.$c["station"].'", "lat":"'.$c["lat"].'", "lon":"'.$c["lon"].'", "total_sp":"'.$c["species"].'", "Species":[';
While ($s=mysql_fetch_assoc($resObs)){
if ($s["station"]==$c["station"]){
$observations.='{"common":"'.$s["species"].'", "number":"'.$s["number"].'", "code":"'.$s["code"].'", "scientific":"'.$s["scientific"].'"},';
}
}
$myJSON.=rtrim($observations, ',').']}},';
$observations="";
mysql_data_seek($resObs,0);
}
$myJSON=rtrim($myJSON, ', ').']});';
return $myJSON;
}
Step 3 - Emit JSON
This is the easiest part of all. Just return the JSON string using whatever the response function is for the language you are using. PHP can use the HTTP Send Request / Get Response functions to give you more control & learn more about what is happening on either side of the exchanges. But, I kept it simple and just echo-ed the string.
echo getData($cid,$myYear,$func);
NEXT STEPS:
>Make & Handle request in your webpage/webapp
>Create mashup
Day 2 , Wed Mar 21
I was really hanging this morning, for someone who has a beer or glass of wine only occasionally, going beer for beer with professional buisness travelers is never a good idea. But it was free and pretty decent beer so I can't complain too much.
General Thoughts
I got a whole lot more out of today's sessions and meetings than the 1st day. The talks I attended went into more depth, the .Net SIG was good, and I took Brian & James's advice and spent more time talking with devs in the Community center.
A few interesting things I learned in these informal conversations
Sessions Attended