Tuesday, June 23, 2009
Norfolk AIR is going Mobile! BlackBerry baby.....
We decided to go all out and build an ASP.NET using the existing classes of the main Norfolk AIR app and retool the interface for the BlackBerry mobile devices.
It turned out better than we could have imagined.
Norfolk AIR Mobile was born..............................
We had to strip out all of the cool autocomplete functionality of the original AIR application as the BlackBerry don't support that level of javascript (at least not all of the BlackBerry that we support anyway).
The user still gets the same type of interface except it is spread across multiple aspx pages instead of the cool autocomplete but hey....its on a BlackBerry.
So here we are simulating a user typing in 400 and pressing go. If they have the full address they can just put it in and get the results.
Once the system gets and input it tests the database to see if it can find the record. If it does it just returns the results for the address. If a record isn't found it will query the database just like the autocomplete function in the main AIR app. It is actually using the same web service. Man this stuff is cool.................
Here are the results returned to the device for the user to narrow
The system only returns valid address so the user can't misspell or get the input wrong. <---making it simple for the users -->
Here is the results screen
Currently we are only serving back a subset of the database during testing.
Our plan is to push out all of the AIR data to the handhelds.
-cj
Wednesday, March 18, 2009
Norfolk AIR imagery interface window
We were really worried about the amount of real estate 2 map windows would consume but I think the interface guys did a brilliant job on the layout and our users are raving about it.
So know that we have two it opens up a whole lot of functionality that would've been hard to accomplish in a single map frame design.
I give you the imagery interface window..........................
The interface consist of an ESRI .NET arcserver mapping control, an ASP.NET panel control and a standard ASP.NET dropdownlist.
First order of business was to keep both the mapping interface (discussed in the next post) and the imagery interface in sync. Things like pan one and the other follows. Zoom one and the other follows. etc.
This was accomplished with a little code behind in the Map_ExtentChanged on the server.
Protected Sub Map1_ExtentChanged(ByVal sender As Object, ByVal args As ESRI.ArcGIS.ADF.Web.UI.WebControls.ExtentEventArgs) Handles Map1.ExtentChanged
Dim env As ESRI.ArcGIS.ADF.Web.Geometry.Envelope = args.NewExtent
Dim env2 As ESRI.ArcGIS.ADF.Web.Geometry.Envelope = Map2.Extent
If env2 IsNot Nothing Then
If Not Map2.Extent.IsEqual(env) Then
Map2.Extent = env
Map1.CallbackResults.CopyFrom(Map2.CallbackResults)
End If
End If
End Sub
The above code will basically eval the current extent of the control and test to see if it is equal to the other map control. Although this isn't dynamic (ie both maps move at the same time) it is pretty quick and the users haven't complained. In the future I may look at seeing if this can be done dynamically. So for know a callback is required.
So what does this window do?
It allows users to toggle 4 years of imagery(we are adding more), display the location in nice google streetview (nice) and fire of a sub window that will tile all years of imagery together.
As the dropdown is changed, imagery requests are sent as an AJAX call to the server that updates the MapResource for the ArcGIS server Map control. When this was first developed it was a postback(slow, refresh, click, yuk). Introducing AJAX allowed us to just target this specific map control without any refresh. We used the standard callback framework for the map controls that ESRI provided and built a PageLoadingHandler. This was a bit tricky as sometimes the callback wouldn't result in a map change(ie show the google window) so we are building the callback and then evaling it on the client. If it is just a standard map callback then javascript just runs it but if it is something else (ie google, errors, open the all image subwindow) then javascript is used to handle those request.
function PageLoadingHandler(sender, args) {
var dataItems = args.get_dataItems();
if (dataItems['Map2'] != null)
//alert(dataItems['Map2']);
//alert(dataItems['DropDownList1']);
{
if (dataItems['Map2'] == 'error')
{
errorHandler();
}
else if (dataItems['Map2'] == 'showstreetview')
{
loadStreetView();
showStreetView();
}
else if (dataItems['Map2'] == 'allimage')
{
var w = dataItems['DropDownList1'];
// alert(w);
showAllImage(w);
}
else
{
hideStreetView();
processCallbackResult(dataItems['Map2'], 'Map2');
}
}
}
So as you can see from the above javascript, that we handle pretty everything in the AJAX call with javascript. We can call our subfunctions, like if the user has selected 'Block View' the server will pass a statement 'showstreetview'. This will be caught by the PageLoadingHandler and will call the loadstreetview() and showstreetview() functions.
In this case it is a nice overlayed div with the current address in google streetview
We have found using the geocoder from google that sometimes are house is down the street thus the term "Block View". We are researching using an address location and converting the geometry to pass the exact location to google but for know it is "Block View".
For the future we are looking at integrating Pictometry images into this window as well being able to see a 360 (north, south, east, west) oblique image of the property.
At first I was a bit skeptical about the 2 window design but I must say it has turned out to be one of the most productive designs that I have run across.
-chris
Tuesday, March 3, 2009
Front End Search for Norfolk AIR
This little beauty is probably the most significant part of the website. Simple, straightforward, so easy anyone can use it. Just start typing and we will try and find it for you.
How was it built?
The search box is actually just a simple asp.net textbox with an ajax autocomplete extender, a textboxwatermark extender and and a hiddenField control all wrapped into one big user control called SearchAssist.
Tied to the search AutoCompleteExtender is a web service that we use to filter the results to the text box (as well as a service other application searchs).
So it goes like this. As the user types the autocomplete extender is listening for the 3rd keystroke. Once the 3rd keystroke is complete the system accesses the webservice to try and retrieve the qualifying values. If it finds some it will return a maximum of 10 records that the user can select from. Pretty simple!
Yes and No
The pretty yes. The simple no.
Behind the scenes we have 3 different sql databases that this search pulls from. (master address, gis (gpins) and real estate (account number)). Actually we are getting ready to add a 4th common place name. Anyway, so as the user is searching the web services is trying to locate like values from 3 different database and present them back to the user...err.. quickly. The quickly part is the key. How do you quickly query 3 database (minimum of 100,000 records each) as fast as the user can type? yeh. The answer is you don't. To overcome the latency problem we actually front ended the queries with a bunch of logic to try and determine if we can narrow down the search before querying the server. Things like if the 1st character isn't a number then I know I don't have to search the address database. If I know I have 5 numbers in a row I don't look for a house number it is probably a gpin or account.
Although a big pain to come up with this was a lifesaver in terms of speed. As the project has progressed I have been able to refine this logic more and more so the query is usually just to one database. Oh yeah did I mention the indexing?
The next big hurdle was know that the user has found a value that they were looking for how does the system know which database that it came from. Don't want to perform all those searches again do I?
Ajax's autocomplete extender actually has a nice feature that I didn't find a lot of documentation on but was able to implement in that you can not only set the returned item name but you can also tie a value to it. This worked out nice as we were able to not only return the search values but also the type(which database they came from)
items.Add(AjaxControlToolkit.AutoCompleteExtender.CreateAutoCompleteItem(dt.Rows.Item(i).Item(displayField).ToString, searchType))
Once the user selects an item from the list we run a little bit of javascript code embedded in the user control(OnClientItemSelected) that will place the database type to a hidden value (thats what the hidden field on the control is for) on the form and presto. We know have the value the user was looking for as well as what database it came from all accessible on the server.
//function SearchSelect( source, eventArgs )
//{
//alert( " Key : "+ eventArgs.get_text() +" Value : "+eventArgs.get_value());
//typeSearch = $get("<%=typeSearch.ClientID %>");
//typeSearch.value = eventArgs.get_value();
//}
That is it. How we were able to produce a really quick, easy to user interface that allows you to search our entire system. The search is so fast that I haven't had anyone be able to out type it yet.
cj
Wednesday, December 31, 2008
NORFOLK AIR is alive!
IT's ALIVE
So we rolled out the Norfolk Air project just before the holidays to the intranet (All City). It couldn't have went smoother. After 4 long hard months of banging out code we were able to rollout a product that will benefit just about everyone in the city. On the very first day the app service about 2000 unique visitors to the tune of about 21,000 requests.
These numbers dwarf any major web app rollout that has occurred by far.
Here are some teaser shots. In the next few post I will be going over the technologies that we used and how we integrated into gooogle, ArcGIS server and AJAX.
Happy Holidays.
Friday, April 25, 2008
Everything FormView
The detailsview is the quick and dirty way of display a list of fields and there corresponding values. It gives you the ability to delete, edit and create records in a nice simple top to bottom display. In making a quick editing form there couldn't be an easier way. But lets say you need to customize how the record is displayed to the user. A couple of rows? Need the custom form handling or AJAX? The detailsview will fall short. Enter the Formview
The formview is much like the detailsview in that it handles the display of a single databound record to the user for delete, edit or create but gives the developer the ability to customize how the user sees the data in the form of templates. When a datasource is bound to a formview a generic set of templates is created for the display, edit and insert states. A developer can arrange or customize these templates to meet any requirements for customized field display.
In the next view post I will be examining the formview and all it has to offer.
-cj
Wednesday, October 3, 2007
Open window from ASP.NET GridView control select event
With a little code behind and some javascript no problemo.......
Using the GridView SelectedIndexChanged event
Protected Sub GridView1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles GridView1.SelectedIndexChanged
End Sub
As part of the ASP.NET GridView you have access to the Gridview.SelectRow property. Not to be confused with a DataRow the GridViewRow returned is just a collection of the cells in the table made up for that particular row. No columns or searchs based on field name or anything like that :(
This is particularly helpful if you want to grab things to pass to the new window as part of the querystring like ID's or other unique identifiers.
So now we can add some code to grab some of the values.
Remember that these are integer based cell call 0-n~
Back to the task at hand
Protected Sub GridView1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles GridView1.SelectedIndexChanged
Dim selectedRow as GridViewRow = GridView1.SelectedRow
Dim myID as string = selectedRow.Cells(1).Text
Response.Write("<script language=""javascript"">window.open('default.aspx?recno=" + myID + "');</script>")
End Sub
Notice that we are getting the value of the 2nd cell (selectedRow.Cells(1) from a zero based array) and passing a javascript line that is wrote on the close of the page. This will allow pass values between pages in your ASP.NET application fired from the Select event of a GridView.
This will know fire a new window with the arguement "recno" in the request string.
Aint ASP.NET grand?
-CJ