23 April 2010

Tool to find maps KML Lat/Lng

Use this tool if you want to find a Google Maps KML GLatLng location, eg to use within your own code: http://www.phdcc.com/GoogleLatLng.htm Either drag the initial marker or enter a search term. The latitude and longitude values are listed below, to 6 decimal places of accuracy.

The map is centred on the UK, as this is where I will want to use it most. It uses the Google AJAX Search API to work out UK postcodes well. Look at the source to see how it works.

This is based on the code by cmarshall at http://www.webmasterworld.com/xml/3542700.htm

19 April 2010

DNN5 User soft-delete issues

In DotNetNuke DNN5 when you delete a user that login is no longer removed from the system. Instead they are soft-deleted, ie a new IsDeleted flag column is set in the UserPortals table. (Note that IsDeleted in the Users table is *not* set - is this ever set?)

In DNN5, code that calls DotNetNuke.Entities.Users.UserController.GetUser() etc will return a UserInfo object even if the user is deleted. Therefore you may have to check the UserInfo.IsDeleted property every time you get a user.

I would have not have implemented it this way. I'd keep the DNN4 functionality and have extra API calls to find deleted users. I wonder: does the DNN core code always check IsDeleted now?

Anyway, UserInfo.IsDeleted is not available in DNN4. As I do not want different versions of my code for DNN4 and DNN5, I have written this isUserDeleted() static method that uses reflection to detect if the IsDeleted property is available and if so calls it. It returns true if the UserInfo is null. After that, for DNN4 it always returns false.

public static bool isUserDeleted(UserInfo ui)
{
if (ui == null) return true;
try {
Type tUserInfo = ui.GetType();

PropertyInfo piIsDeleted = tUserInfo.GetProperty("IsDeleted");
if (piIsDeleted != null)
{
bool IsDeleted = (bool)piIsDeleted.GetValue(ui, null);
return IsDeleted;
}
}
catch (Exception) { }
return false;
}

10 April 2010

Freegle Find a Group mashup

This blog describes how I've used one JavaScript script to power several different incarnations of a Google Maps tool to help people Find a Freegle Group. Usage instructions for the Find a Group tool are on the Freegle wiki. You can also see the Find a Group gadger lower down on this page on the right.

This is primarily a technical article - for usage instructions, see the above link.

Freegle Find a Group screenshotFreegle is a network of local re-use groups in the UK. Group members give away and receive any unwanted items for free. This helps conserve the world's resources, clears clutter and helps others.

The Find a Group tool shows a UK map with pinpoint markers for each Freegle group. You can move around and zoom in as normal - clicking on a marker shows an information bubble - clicking on the link in the bubble takes you to the group web site.

Find a Group also has a search box. The tool uses Google to search for that location in the UK. If found, the tool zooms to that location and adds a marker - you are invited to look for the nearest Freegle group marker. The nearest groups are listed below

Source data

The green group markers come from this KML file - maintained by another Freegle member. This is the latest data so the tool should always be up to date.

Actually, that's not the whole truth. The tool needs to load the KML file into memory so it can search for groups. Loading the above Google Maps KML link doesn't work because of cross-site scripting security (as I understand it). Therefore the code loads a separate snapshot of the KML file from a URL that it can access.

There's a plan to resolve this issue by having the primary KML file on the iLoveFreegle.org servers. That way, my code would only need to reference one file. And the plan is to have this file generated directly from the main Freegle groups database, so it is always up to date. (Currently the Google Maps KML file has to be updated by hand.)

Mashup variants

(P) Full web page

The Find a Group tool occupies a full web page here: maps.ilovefreegle.org.

(M) Google Mapplet

The Find a Group tool appears in the Google mapplets directory here. When added to My Maps it shows filling the browser window.

(I) Iframe

The Iframe version of the Find a Group tool is designed to fit into a small window within another page. The usage instructions describe the various configuration parameters.

You can see the iframe in action at the Penrith and Eden District Freegle group and its associated helper web site.

(G) Google gadget

The Find a Group tool is packaged as a Google Gadget, defined here. You can therefore add it to iGoogle, add it to a blog, and get the code to put on a web site. The gadget has similar options to the iframe.

The gadget has a Content type of url, with the main gadget code here. The gadget options are passed as URL QueryString parameters.

(F) Facebook application

The Find a Group tool is being developed as a simple Facebook iframe application.

Common Page Layout

Each of the above tools has HTML which has the same page elements:

  • Map div called "map_canvas"
  • Noscript block
  • Div called "jsBlock" that wraps the search form
  • Input text box called "search"
  • Submit button that has an onclick handler "return doSearch();" that always returns false
  • Information div called "idInfo"
  • Debug div called "idDebug" that shows version info by default

Accessibility

The tool has a noscripts block that has a link to the main site groups list - this is shown if the browser has no JavaScript or it is turned off. However Chrome doesn't show this text if JavaScript is turned off - sounds like a bug to me.

The "jsBlock" search form div is not displayed by default; the JavaScript makes this block visible. This avoids confusion if JavaScript is not available.

The search text box has an accesskey of "4" defined. For the larger variants, a label is defined for this text box.

The tool HTML is hopefully valid XHTML and CSS.

API and api keys

The Google Maps/Mapplets API is used in the JavaScript. There are various differences depending on whether it is a mapplet or not. Therefore one of the JavaScript initialise() parameters is a mapplet boolean; this is stored in a global for later use throughout the code.

To use the Google Maps API, each site needs an API key. All these tools run on the maps.iLoveFreegle.org domain so the iLoveFreegle.org API key is used. The mapplet code does not need an API key.

JavaScript structure

The JavaScript is pretty straightforward procedural code, with some globals, an initialise() function and a function doSearch() that is called when a user does a search. The JavaScript code is here: http://maps.ilovefreegle.org/freegle_maps.js.

initialise() function

This takes parameter specifying whether big map controls are wanted, and the mapplet boolean.

  • If not a mapplet, check that the browser is Google Maps compatible
  • Get references to the "idInfo" and "idDebug" page elements - add the js version to idDebug
  • If the "jsBlock" page element is present, change its display style to block to make it show.
  • Get the map object and set the map height if it has been specified in a parameter.
  • Get the z SearchZoom integer parameter for later use

  • Set the map to be the approx centre of the UK at a suitable scale
  • Use GGeoXml to load the primary KML file and add it as an overlay - the code cannot access the contents of the overlay, so...
  • Initiate a separate load of the (secondary) KML for processing, either using _IG_FetchXmlContent() or GDownloadUrl()
  • Finally set the focus to the search box if required

processKML() function

This parses the received KML XML file and stores info about each Group placemark object in the Groups array.

Note that the KML file stores each coordinate the wrong way round, ie longitude, latitude then height. The GLatLng.fromUrlValue() function needs just the first two values but reversed.

Once the KML is loaded, it sees if a search has been requested and does it.

doSearch() function

The search function does a Google GClientGeocoder search and zooms in to show the result. The search also lists the nearest groups, calculated using GLatLng.distanceFrom().

doSearch() uses showAddressResult(), SearchGroups() and createMarker().

Helper functions

String.prototype.trim, PageQuery, queryString() and isInteger() were obtained from code on the Internet. I added queryString2() helper function.

I have updated PageQuery to cope with URL parameters with key=value pairs where "=value" is missing.