SwarmOnline Menu Mobile
  • Home
  • About
  • Services
  • Portfolio
  • Blog
  • Contact

Recent Posts

  • Launching Sencha Insights
  • Documenting your Sencha apps with JSDuck
  • Localising Sencha Touch and Ext JS applications with Ux.locale.Manager
  • Announcing SwarmOnline’s New Website!
  • Constructing a complex form layout with Ext JS 4

Recent Comments

  • Stuart on Ext.ux.TouchCalendar – A Sencha Touch Calendar component
  • Stuart on Localising Sencha Touch and Ext JS applications with Ux.locale.Manager
  • Manuel Rodriguez on Localising Sencha Touch and Ext JS applications with Ux.locale.Manager
  • Remi Bloch on Ext.ux.TouchCalendar – A Sencha Touch Calendar component
  • Remi Bloch on Ext.ux.TouchCalendar – A Sencha Touch Calendar component

Archives

  • March 2013
  • February 2013
  • December 2012
  • September 2012
  • August 2012
  • November 2011
  • October 2011
  • May 2011
  • March 2011
  • February 2011
  • January 2011
  • November 2010

Categories

  • Development Tips
  • Ext JS
  • News
  • Sencha Touch
  • Sencha Touch Extensions & Plugins
  • Web Design
  • Web Design & Development Blog

Ext.ux.touch. MapLoader : Dynamically load map points as you pan around a map

6
31 Jan
2011
MapLoaderScreenshot

Sencha Touch Extensions & Plugins

Web Design & Development Blog

Overview

This plugin allows you to have your map dynamically load points of interest as the user pans around the map allowing you to minimize the amount of data you have to fetch from the server and minimize the number of markers on the map speeding up performance by only dealing with points that are within the map’s visible area.

Extract the ZIP file into the ‘examples’ directory of the Sencha Touch libary package.

Download - Ext.ux.touch.MapLoader (80kb) (995)

Demo – Ext.ux.touch.MapLoader

This demo loads randomly generated points onto the map as you pan and zoom. The markers are numbered based on the batch that they were loaded in to give an idea of the plugin working.

The Plugin

The plugin monitors the user’s panning and zooming and triggers a load of the specified store or leaves you to implement your own loading functions by hooking into its ‘mapload’ event.

Configuration Options

unitsthe units that you would like to deal in. This can be set to ‘miles‘ or ‘km‘. Defaults to ‘miles‘
bufferTypehow you would like the map’s buffer to operate. Possible values are ‘ratio‘ or ‘fixed‘.This property allows you to include a ‘buffer’ distance to be added to the visible area’s radius allowing extra points to be loaded that won’t be initially visible. This means that when the user pans there are points present immediately before the full load is complete, making the user experience much more seamless.fixed: this means that the value of the ‘buffer‘ config is always used as the buffer distance no matter what zoom level the map is on.ratio: by using this setting the value of the ‘buffer‘ config option (set between 0 and 1) is multiplied by the visible area’s radius to get a proportional distance outside the visible area to include in the load.

For example if you are at a zoom level which means a 1 mile radius is visible to the user and you have a ratio buffer value of 0.5 then you will load points within a circle of 1.5 miles. This is useful when you consider a huge change in zoom. If we had a fixed buffer value of 0.5 miles in the previous situation then that would be acceptable, however if the user zoomed out to a visible radius of 200 miles then we would load very few extra points to smooth the load when the user pans. If we had a ratio buffer of 0.25 (25%) then at a zoom level with 1 mile radius we would load 1.25 miles and at a zoom level with 200 mile radius we would load an extra 50 miles giving us much more points outside the visible area to make the pan-load smoother.

bufferthe value that will be used when calculating the Buffer Radius.If bufferType is set to ‘ratio‘ then the value should be a decimal between 0-1 (equivalent to a percentage) or if bufferType is set to ‘fixed‘ then this can be any number in the units defined in the units config.
storeif defined the store will be automatically loaded when needed by the plugin – it effectively hooks up the ‘mapload’ event and loads the store with the correct parameters.If left null, it will be ignored and you must define your loading implementation.
disabledwhen set to true, the plugin will be disabled and no dynamic loading will happen.Can be modified using the enable(), disable() or setDisabled(value) methods
loadIntervalthis config can be used to fire loads while a user pans rather than only at the end of the pan movement.The number of milliseconds between loads while the user is panning.Set to 0 for no intermediate loads to take place.

Events

One event is added to the plugin’s parent Ext.Map object. The ‘mapload‘ event is fired when the user finishes a pan movement (including pauses in movement, not just when the touch is removed) or, if the loadInterval is set, at the defined intervals. The event passes 5 parameters to its handlers:

centrethe map’s current centre. An object in the form { lat: xx, lng: yy }
boundsthe bounds of the map’s current visible area. An object in the form:
{
    northeast: {
        lat: xx,
        lng: yy
    },
    southwest: {
        lat: xx,
        lng: yy
    }
}
boundingRadiusthe radius of the visible map. I.e. the distance from the centre point to the corners of the visible region. Measured in the units provided in the units config.
bufferRadiusthe radius that has been calculated based on the bufferType and buffer config options. This doesn’t get added to the boundingRadius and is applied on the server.
zoomthe current zoom level of the map.

An Example

The plugin itself is very simple with the complexity coming in the implementation of the load and post-load logic (e.g. adding markers etc). I will describe an example using an AJAX request rather than defining a store.

Defining the plugin

{
    xtype: 'map',
    plugins: [new Ext.ux.touch.MapLoader({
        units: 'miles',
        bufferType: 'ratio',
        buffer: 0.25, // calculate 25% of the visible radius,
        loadInterval: 300 // do a load every 300 ms during panning
    })],
    centered: true,
    mapOptions: {
        center: new google.maps.LatLng(55.857809, -4.242511), // centre over Glasgow
        zoom: 15
    }
}

Elsewhere we hook into the Map’s new ‘mapload‘ event and fire an AJAX call to the server passing up all the event’s parameters. The server code (which we’ll look at in a second) responds with a list of lat/lng positions and then our JS adds these points to our map.

// code found within the mapload's event handler
Ext.Ajax.request({
    url: 'server/search.php',
    method: 'get',
    params: {
        centre: Ext.encode(centre),
        bounds: Ext.encode(bounds),
        boundingRadius: boundingRadius,
        bufferRadius: bufferRadius,
        zoom: zoom
    },
    success: function(response){
        var jsonResponse = Ext.decode(response.responseText);
        if (jsonResponse.result) {
            for (var i = 0; i < jsonResponse.result.length; i++) {
                var resultPos = jsonResponse.result[i];
                if (!this.markerExists(resultPos.lat, resultPos.lng)) { // custom method (see full example code) to ignore points we've already loaded
                    // cache the newest point so we don't add it in the future
                    this.markerCache.add({
                        lat: resultPos.lat,
                        lng: resultPos.lng
                    });
                    var pos = new google.maps.LatLng(resultPos.lat, resultPos.lng);
                    var marker = new google.maps.Marker({
                        map: this.map,
                        position: pos
                    });
                }
            }
        }
    },
    scope: this
});

The serverside code simply parses the parameters being passed up and inputs them into a SQL version of the Haversine Formula. It then returns the results as JSON to be consumed by our JS code.

// Get parameters from URL
$centre = json_decode(stripslashes($_GET["centre"]), true);
$bounds = json_decode(stripslashes($_GET["bounds"]), true);
$boundingRadius = $_GET["boundingRadius"];
$bufferRadius = $_GET["bufferRadius"];
$zoom = $_GET["zoom"];
// Opens a connection to a mySQL server
// Set the active mySQL database
// Search the rows in the markers table
$query = sprintf("SELECT lat, lng, ( 3959 * acos( cos( radians('%s') ) * cos( radians( lat ) ) * cos( radians( lng ) - radians('%s') ) + sin( radians('%s') ) * sin( radians( lat ) ) ) ) AS distance FROM markers2 HAVING distance < '%s' ORDER BY distance",
    mysql_real_escape_string($centre['lat']),
    mysql_real_escape_string($centre['lng']),
    mysql_real_escape_string($centre['lat']),
    mysql_real_escape_string($boundingRadius) + mysql_real_escape_string($bufferRadius)); // add boundingRadius and bufferRadius
$result = mysql_query($query);
if (!$result) {
    die("Invalid query: " . mysql_error());
}
$outputArray = array();
while($array = mysql_fetch_array($result)){
    $outputArray[] = $array;
}
echo json_encode(array("success" => true, "count" => count($outputArray), "result" => $outputArray));

To use the ‘store‘ config simply move the ‘success’ function defined above into the store’s load event handler and it will have the same effect.

And that’s us, you should see your points loading as you pan the map!
There are a couple points you must consider when using the plugin and when implementing your surounding code, these are:

  • Duplicate points – it is highly possible (and likely!) that the same point will be included in multiple loads so you must take this into consideration when processing the loaded points to stop markers being added twice.
  • Redundant markers – markers that are off screen are still there so you may want to implement some clean-up code to destroy these markers when not needed to speed up performance.

If you have any problems with using the plugin, have questions about how it works or have suggestions about ways we can make it better then please leave us a comment or drop us an email!

Extract the ZIP file into the ‘examples’ directory of the Sencha Touch libary package.

Download - Ext.ux.touch.MapLoader (80kb) (995)

Demo – Ext.ux.touch.MapLoader

This demo loads randomly generated points onto the map as you pan and zoom. The markers are numbered based on the batch that they were loaded in to give an idea of the plugin working.

 

Tweet

6
  1. Winalot - Reply

    May 9, 2011 at 3:50 pm

    Hi Stuart,

    Great plugin, however I had to change centre.wa and centre.ya to centre.Ea and centre.Fa (same for bounds) before it would work.

    Perhaps this changed in the GMaps implementation?

    Keep up the great work!

    WP

    • Stuart - Reply

      May 9, 2011 at 3:54 pm

      Hi

      Thanks for the feedback! Coincidentally someone else reported this bug this morning also. I will release an update later today to fix it – as you say the centre.**s need updated. It turns out to be an oversight on my part with the Gmaps library, as there is actually ‘get’ methods available to avoid using those 2 character aliases.

      Cheers
      Stuart

      • Stuart - Reply

        May 9, 2011 at 6:30 pm

        I have uploaded a new version of the plugin that will resolve this issue. Thanks for the bug report Winalot (…and Brendan via email!).

        You can download it from the blog article or directly from here

        Cheers
        Stuart

  2. Winalot - Reply

    May 9, 2011 at 6:51 pm

    Hi Stuart,

    Thanks for the update; all working perfectly!

    Regards,

    WP

  3. BrendanMc - Reply

    May 9, 2011 at 6:59 pm

    Hi Stuart,

    I have seen this recently in the Google Maps directionsService API where say
    location.Ca, location.Da changes to location.Ea,location.Fa….argh :-(

    Hence your change to :
    lat: centre.lat(),
    lng: centre.lng()
    I presume these getters are available on all location properties?
    Get work, appreciate the code-sharing and explanations.

    Brendan

    • Stuart - Reply

      May 9, 2011 at 7:03 pm

      Yea I think they are available on all location objects – I didn’t realise they existed initially either so that’s why the original code used the seemingly unstable property names.

      Stuart

Leave a Comment - Cancel reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

About SwarmOnline

Based in Glasgow’s West End, SwarmOnline provide web development, cross platform mobile app development and training services for local and international organisations. We have expert knowledge of Sencha technologies and have been Sencha Partners since 2011. We specialise in creating dynamic, innovative and practical solutions.

Meet the Team...

Newsletter

Subscribe to our newsletter and we’ll keep you up-to-date. We don't do spam.

Get in touch!

Email: info@swarmonline.com   
Skype: andrew-swarmonline stuart-swarmonline   
Phone: 0141 438 2231   


© 2013 SwarmOnline Ltd. All Rights Reserved.

SwarmOnline Ltd is a Limited Company registered in Scotland, with company number SC411633. Our Registered Office is 1-2 249 Byres Road, Glasgow G12 8UB.