Company

Products

Services

Partners

Media

 

 

 

Home

Empress Markets

News & Events


Presentations

Press Releases

Product Profile

Success Stories

Technical News


White Papers

Join Mailing List

For More Info

         

    Empress Technical News -  March 2010

      Interfacing Google Maps with Empress Spatial Technology
BirdWatching Location Intelligent Application

 

Introduction
In the old days, a Geographical Information System (GIS) was the way to provide mapping capabilities integrated with user data. GIS applications would create interactive spatial queries, analyze spatial information, manage spatial information and present the results. Because of the specialized knowledge involved, the GIS vendor would usually create those applications.

Empress Software Inc (ESI) has collaborated in the past with NOAA and GIS vendors such as ESRI and Mapinfo to help them provide GIS applications that utilize database technology. In those cases, an Empress database was used as a storage mechanism for data including geographic information.

In the recent times, the new kids on the block such as Google, Microsoft and Yahoo, provide mapping services to end users. As a result, building location intelligent applications has become much easier.

In addition, integrating a geographical component into business processes and tools is one of the key enabling technologies for business innovation in coming years.

The two essential parts of a typical location intelligent application are:

  • Mapping Service
  • Spatial Database

In this technical note we will use a BirdWatching application to describe how to use the two technologies together to make a location intelligent application tick.

Interfacing Google Maps with Empress Spatial Technology – BirdWatching Application
Around the world, bird watching has become a very popular activity. Bird watchers collect records of their observations and share observation information with others. The bird observation data usually includes the name of birds spotted, geo location, time of sightings, the name of observer(s), etc.  The bird observation data is accumulated in various formats, such as spreadsheets, text files and relational databases. Bird watchers have logged millions of bird observation events and continue to log more.

Geo-location is a very important aspect of a bird observation. It requires a database that can handle huge amounts of data, and provide optimized spatial searches.

The BirdWatching Application has been developed using Empress Database and Google Maps API to allow users to make queries of observed bird data based on geo-locations. It allows users to view and explore bird data using a browser based interactive map on the Internet.

Empress Database uses highly efficient R* tree index for fast execution of geo-location based queries. Empress Database’s flexible API makes it easy to glue Empress database access code with Google Maps API to quickly build visual web applications.

In this technical note, we discuss the implementation of the BirdWatching application that integrates geo-location data and a spatial search from Empress Database with the Google Maps API.

First, the application allows bird lovers to search global locations for bird observation events and see the results on Google Maps.

 Querying Birds Observations by Location

Figure 1: Querying Birds Observations by Location

In Figure 1, an user can search bird observation events by specifying an address and a radius. The address can be a street address, a city name, a park name or any addresses acceptable to Google Maps. The query result is displayed by showing each bird observation on the map at the location where the event took place. To get the observation details, a user can click on the place marks and the names of birds spotted at the location will be displayed along with other information.

The application also enables users to report bird observation events by clicking on the map to specify the location of the event and provide details.

Reporting a Bird Observation

Figure 2: Reporting a Bird Observation

Figure 2 shows how users submit a bird observation. When the user saves the observation, the data is stored in the Empress database and becomes instantly available to other users around the world.

Implementation of the BirdWatching Application
We will look at the BirdWatching application starting with its Architecture and then moving on to Database Schema, Empress Database CGI Program, Client Side Programming and Conclusion.

BirdWatching Application Architecture

Architecture of the BirdWatching Application

Figure 3: Architecture of the BirdWatching Application

In the Figure 3, the application includes server side and client side components.

The server side consists of an Empress database providing storage and indexing of the bird observation data and a HTTP server that invokes a CGI program upon users’ requests. The CGI program processes users’ requests, communicates with the Empress database and returns the results back to client through the HTTP server.

The client side is composed of HTML and JavaScript running within a web browser. It uses JavaScript Ajax (asynchronous JavaScript and XML) to communicate with the server side CGI program to submit queries and get results back packaged as a JSON (JavaScript Object Notation) object.

The client side also calls Google Maps API with the application data to display observation data on the map. The actual map images and map control functions are from Google Maps servers. The JavaScript code is also used for interacting with users.

 

The Database Schema
The bird observation data of this application is stored in two Empress Database tables. The first table, bw_events, contains general information about bird observation events, such as event id, event time, event longitude and latitude.

 

***  Table: bw_events  ***

        Attributes:
          event_id                                                  character(8)
          event_lat                                                double precision
          event_lng                                              double precision
          event_time                                          timestamp(0)
          count_type                                          character(4)
          effort_hrs                                              float(24)
          distance                                                float(24)
          effort_area_ha                            float(24)
          observer_id                                    character(12)
          observers_no                              integer

          Indices:        RSTARTREE geoindex ON (event_lat, event_lng)

The attributes event_lat and event_lng represent the exact geographic location of an event. Their values are used by Google Maps API to locate the observation event on the map.  The R* tree index (spatial index) defined on the two attributes is used to speed up spatial queries.

For each observation event, there might be more than one bird observed out of thousands bird species. We need another table to store bird names associated with each event. The table, bw_bird is defined as follows:

 

      ***  Table: bw_birds  ***

        Attributes:
          event_id                                                    character(8)
          bird_name                                            character(32)
          bird_no                                                      character(4)
       
        Indices:        NORMAL(2, 15) BTREE indx1 ON (event_id)
                                      NORMAL(2, 15) BTREE indx2 ON (bird name)

The two tables are linked to each other by the event_id attribute with a one-to-many relationship.

 

                                                                                                      The Relationship Between bw_events and bw_birds

Figure 4. The Relationship Between bw_events and bw_birds

 

The Empress Database CGI Program
The Empress Database CGI program uses CGI protocol to receive Ajax HTTP request from client side, queries the database using Empress C/C++ MR routines (API calls) and composes Ajax HTTP responses to the client side with the result of a query. It takes following steps to complete the search of all bird observation events in a certain area.

  1. It parses HTTP requests from the client side to get the latitude/longitude values of the geographic center of the area and the radius of the area.
  1. It calls Empress Database Spatial Search function    ms_rstree_point_search_in_circle() with the latitude, the longitude and the radius to find a list of all event records that are contained in the specified circle area.

 

  1. It uses the event record list from the previous step to retrieve observation data from bw_events and bw_birds tables for each event in the area.

 

  1. It assembles the resulting bird observation event data into a JSON literal string and sends it back to the client side as in an HTTP response.    Due to its simplicity, JSON is natively supported in all major web browsers and widely utilized in web applications.

 

The following is the part of the program source code:

 

void search_birds ()
{
         
void* mr_event
         
void* mrr_event
         
void* id_attr
         
void*           lat_attr
         
void* lng_attr
         
void* time_attr
             
void*    observer_id
         
void* mr_birds
         
void* mrr_birds
         
void* bid_attr
         
void* bname_attr
         
void* bnum_attr

         
void*           qual
         
void* qual2
         
void* q

             
void*    retrieve_events
             
void*    retrieve_birds
         
void* rshandle
             
long*   rec_list
         
double          circle[3]

         
int   i

     
msinit ()

         
mr_event = mropen(DATABASE, " bw_events" , 'r')
         
mrr_event = mrmkrec (mr_event)
         
id_attr = mrngeta (mr_event, " event_id" )
         
lat_attr = mrngeta (mr_event, " event_lat" )
         
lng_attr = mrngeta (mr_event, " event_lng" )
         
time_attr = mrngeta (mr_event, event_time"
         
mr_birds = mropen(DATABASE, " bw_birds" , 'r')
         
mrr_birds = mrmkrec(mr_birds)
         
bid_attr = mrngeta (mr_birds, " event_id" )
         
bname_attr = mrngeta (mr_birds, bird_name"
         
bnum_attr = mrngeta (mr_birds, bird_no" )
         
rshandle = ms_rstree_open (mr_event, lat_attr, lng_attr, 0)
     
if (rshandle == 0)
         
{
                                 
fprintf (stderr, " cannot open rstree index \n" )
                                 
goto done
         
}

         
circle[0] = lat
         
circle[1] = lng
         
circle[2] = radius / 111
         
fprintf (stderr, " Searching event in (%f, %f, %f). \n"
                                             
circle[0], circle[1], circle[2])
         
rec_list = ms_rstree_point_search_in_circle (rshandle, circle)
         
printf (" {\" events\" :\n"
         
printf (" [\n" )
         
if (rec_list == 0)
         
{
                                             
fprintf (stderr, " No event found in (%f, %f, %f). \n" ,
                                                                                 
circle[0],
                                                                                 
circle[1],
                                                                                 
circle[2])
                             
goto done
         
}


         
qual = mrqlst (mr_event, rec_list)
         
if (! qual)
         
{
                     
printf (" %s\n" , mrerrmsg())
                     
return
         
}


     
retrieve_events = mrgetbegin (qual, mrr_event, (void 0)
         
i = 0

         
while (mrget (retrieve_events) == 1)
         
{
                                 
char* eid
                                 
double*        lat
                                 
double*        lng
                                 
int j
                                 
if (i > 0)
                                             
printf (" ,\n" )
                                 

                     
printf (" {\n" )
                                 
eid = mrgetvs (mrr_event, id_attr)
                                 
qual2 = mrqseq (bid_attr, eid)

                     
          printf (" \" eid\" : \" %s\" ,\n" , eid)

                                 

                                 
printf (" \" lat\" : \" %f\" ,\n" ,
                                             
*(double*)mrgeti (mrr_event, lat_attr))
                     


                                 
printf (" \" lng\" : \" %f\" ,\n" ,
                                             
*(double*)mrgeti (mrr_event, lng_attr))

                                 
printf (" \" time\" : \" %s\" ,\n" ,
                                             
mrgetvs (mrr_event, time_attr))
                                 

                                 
retrieve_birds = mrgetbegin(qual2, mrr_birds,           
                                 
if (! retrieve_birds)
                                 
{
                                             
printf (" %s\n" , mrerrmsg())
                                             
break
                                 
}
                                 
j = 0
                                 
while (mrget(retrieve_birds) == 1)
                                 
{
                                             
if (j > 0)
                                                         
printf (" ,\n" )
                                             
else if (j == 0)
                                             
{
                                                         
printf (" \" birds\" : [\n" )
                                             
}
                     
          printf (" {\n" )
                                             
printf (" \" name\" : \" %s\" ,\n" ,
                                                         
mrgetvs (mrr_birds, bname_attr))
                                             
printf (" \" number\" : \" %s\" \n" ,
                                                         
mrgetvs (mrr_birds, bnum_attr))
                                             
printf (" }\n" )
                                             
j++
                                 
}

                                 
if (j > 0)
                                             
printf (" ]\n" )

                                 
printf (" }\n" )

                                 
mrgetend(retrieve_birds)
                                 
i++
         
}

         
mrgetend (retrieve_events)
         
fprintf (stderr, " %d events found\n" i)
         
ms_rstree_list_free (rec_list)

done:
         
printf (" ]\n" )
         
printf (" }\n" )
         
mrfrrec (mrr_event)
         
mrfrrec (mrr_birds)
         
mrclose (mr_event)
         
mrclose (mrr_event)
         
ms_rstree_close (rshandle)
         
msend ()
}

 

An HTTP request from the client could look like:

http://localhost/cgi-bin/birdwatch/searchdb?lat=25.7742657& lng=-80.1936589& radius=6.

It asks for finding all observation events inside the circle with the center of 25.7742657, -80.1936589, (Miami FL) and a radius of 6 kilometers.

The following is the body of an HTTP response generated by the CGI program. It shows the bird observation event happened at (25.736817, -80.156517) and two kinds of birds were spotted at the location: macroura and Cardinalis cardinalis.

  {" events" :
      [   
            {       
            " eid" : " S3646552" ,
       
    " lat" : " 25.736817" ,
       
    " lng" : " -80.156517" ,
       
    " time" : " 2008-03-01 12:45:00" ,
       
    " birds" : [
              {
             
      " name" : " Zenaida_macroura" ,
               
    " number" : " 22"
             
},
             
{               
                  " name" : " Cardinalis_cardinalis" ,
               
    " number" : " 2"
           
}
       
]
   
}
 
]


Client Side Programming
Client side programming is a combination of HTML and JavaScript code running inside a web browser, (Chrome, Firefox, Safari and IE8).

The client side uses Ajax, JSON and Google Maps API to do the following tasks:

  1. When a user types in an address on the web page, it uses Ajax calls to send the address to the Google Maps geo-coding service to search for the address and translate it into associated geographic coordinates as latitude and longitude.
  1. It then sends the geographic coordinates and the user inputted radius value to the CGI program on the server side as HTTP requests. For example,

                      http://localhost/cgi-bin/birdwatch/searchdb?lat=25.7742657& lng=-80.1936589& radius=6

  1. Upon receiving the HTTP response from the server side, it parses the data embedded in the response into a JavaScript JSON object, which represents all events in the search area. It then retrieves geographic coordinates of every event from the object and passes them to the Google Maps API to show each bird observation event.
  1. Responds to users’ interaction. When a user clicks on a mark on the map, it responds by displaying details of the event in a pop-up window on the map. When the user zooms in and out of the map, it adjusts the number of events to be displayed on the map to reduce visual clutters.

Parts of the client side code is as follows:

  // load map
function load() {
         
if (GBrowserIsCompatible()) {
             
geocoder = new GClientGeocoder()
             
map = new GMap2(document.getElementById('map'))
             
map.addControl( GLargeMapControl())
             
map.addControl( GMapTypeControl())
             
map.setCenter(new GLatLng(40, -100), 4)
                     

                     
var icon = new GIcon(G_DEFAULT_ICON)
       
      icon.image =
" http://chart.apis.google.com/chart?cht=mm& chs=24x32& chco=FFFFFF,008CFF,000000& ext=.png"
         
}
     
}

function searchLocations() {
       
var address = document.getElementById('addressInput').value
       
geocoder.getLatLng(address, function(latlng) {
           
if (!latlng) {
               
alert(address + ' not found')
           
} else {
               
searchLocationsNear(latlng)
           
}
       
})
   
}

   
function searchLocationsNear(center) {
       
var radius = document.getElementById('radiusSelect').value

       
var searchUrl = '/cgi-bin/birdwatch/searchdb2?lat=' + center.lat() + '& lng=' + center.lng() + '& radius=' + radius + bn=xxx'

       
GDownloadUrl(searchUrl, function(data) {
           
var jsondata = eval('(' + data + ')'
           
var markers = jsondata.markers

           
map.clearOverlays()
         
    if (markerCluster != null)
         
                    markerCluster.clearMarkers()

           
var sidebar = document.getElementById('sidebar')
           
sidebar.innerHTML = ''
           
if (markers.length == 0) {
               
sidebar.innerHTML = results found.'
             

           
map.setCenter(new GLatLng(center.lat(), center.lng()), 11)
           
}

           
var bounds = new GLatLngBounds()
         
    var gmarkers = []
           
for (var i = 0 i < markers.length i++) {
               
var name = markers[i].eid
               
var point = new GLatLng(markers[i].lat, markers[i].lng)
               

                     
  var bds = markers[i].birds
                     
  if (! bds)
                                 
continue
               

               
var mhtml
                     
  mhtml = < ul> "
               
for (var j = 0 j < bds.length j ++) {
                       
mhtml = mhtml + < li> " + bds[j].name + " (" bds[j].number + )" + " < /ui> "    
               
}
               
mhtml = mhtml + " < /ul> "
               
var marker = createMarker(point, name, mhtml)
                     
  gmarkers.push(marker)
               
// map.addOverlay(marker)

               
var sidebarEntry = createSidebarEntry(marker, name, '', '')
               
sidebar.appendChild(sidebarEntry)
               
// bounds.extend(point)
           
}
         
   
                     
var gct = createMarker(new GLatLng(center.lat(), center.lng()),
                                             
'Search Center: (' + center.lat() + ', ' +
                                             
center.lng() + , ''
                     
map.addOverlay(gct)
                     
markerCluster = MarkerClusterer(map, gmarkers,
                                 
{maxZoom: 12, gridSize: 30})
         
 
           
map.setCenter(new GLatLng(center.lat(), center.lng()))
       
})
   
}

 
function createMarker(point, name, birds) {
         
var marker = new GMarker(point)
         
var html = '< b> ' + name + br/> ' + 'Birds Observered: ' + /b> < br/> '
                     
+ '< div style=" overflow: auto height:100px" > ' + birds + '< /div> '
         
GEvent.addListener(marker, 'click', function() {
 
            marker.openInfoWindowHtml(html)
         
})
         
return marker
}

 

Instead of Conclusion
The BirdWatching application shows how Empress Spatial index search functionality is integrated with Google Maps API and other web technologies. Using this technology stack, application developers can build high performance, geographical data-driven applications with a top class user experience.

Google Maps API & Empress Spatial Technology are essential components for making location intelligent applications do what they should.

 

Empress Software Inc.
www.empress.com

 

                       
                             
                         

Company
Information
GSA Contracts
Careers
Privacy Policy 
Contact US

Products
Empress RDBMS
Empress Servers
Empress API
Free Trial 
Empress iPad Apps
 

Services
Consulting
Mobile Apps Development
Technical Support

Training Courses
Empress Extreme  

Partners
Distributors
Business Alliances
Partner Programs  
GSA Program
Distributors Program

Media 
Empress Markets
News and Events
Press releases
Technical News
Success Stories

        USA: 301-220-1919                 Canada & International:       905-513-8888         
Copyright © 2014           Empress Software Inc.           [email protected]           Join Empress Mailing List