Duncan's blog

October 29, 2016

Crossword solver

Filed under: Javascript,jQuery,Web — duncan @ 4:12 pm

Here’s a little something I built for myself, with the intention of helping to complete crosswords. It’s quite annoying when you’ve got 80 – 90% through one and with only a few obscure words left to complete.

This lets you:

  • create a blank crossword
  • fill out any letters in it you’ve already got
  • look up words for any you’ve only got some of the letters for

It doesn’t as yet let you save them and re-load.

The front end uses jQuery; server-side it’s using ColdFusion, with a dictionary of 90,000+ words.

Anyway, here it is; let me know any feedback:

http://www.duncancumming.co.uk/crossword/

June 17, 2016

Google Maps – displaying lots of markers

Filed under: Google Maps,Javascript — duncan @ 8:00 am
Tags: , , , , ,

I was inspired by this amazing site by Jill Hubley which uses publicly-available data of all the street trees in New York City, to see if the same data was available on the London DataStore.  I couldn’t find the same thing for the whole city, although I did find just two of the 33 borough councils had published this data on the nation-wide data.gov.uk site (but not the London-specific DataStore for some reason).

However, while looking for the trees data, I did find a dataset of all the allotments in London.  Pretty exciting stuff, I think you’ll agree, and something I’m sure something everyone wants to see mapped.  And I thought it might be an interesting exercise in seeing how to handle slightly larger amounts of markers than I usually do, so…

Firstly, they provide the data in three different files.  Two of them are zipped bundles containing a bunch of files in formats that aren’t familiar to me, but I assume are standard for GIS software used across the public sector.  The third is just a plain-old CSV file, so that’s what I’ve gone for.

There are 741 allotment locations listed.  I’ve imported that into a Google Spreadsheet, and got rid of the columns I didn’t think were necessary.

So initially I just want to grab all this data, and add it to a Google map as standard markers with infoWindows attached.  Nothing new here.  I exported my slightly amended version of the spreadsheet as a new .csv file, then used this site here to convert that into JSON structure.  That did a pretty nice job of giving me an array with each allotment being an object like this:

{
    "Name": "Abbots Way",
    "Location": "Alongside railway line",
    "Borough": "Bromley",
    "Organisation": 0,
    "Facilities": 0,
    "Comments": 0,
    "Latitude": 51.393386,
    "Longitude": -0.047422
}

For the fields Organisation, Facilities and Comments this data seemed to be fairly inconsistent across the various councils, but mostly they were blank, which has ended up as zero in the JSON.

I then simply set this up as a JSON structure, and minified it using this site (reducing it from 206Kb to 103Kb).  At this point it makes sense to get the data via AJAX, rather than embed this as a giant variable into my javascript code.  I’m using jQuery’s $.ajax() method instead of their $.getJson() method, just because I’m running this locally, not using a webserver, and I needed to specify the mimetype using the beforeSend callback (thanks to this answer on StackOverflow).

$.ajax({
	dataType: "json",
	url: 'allotments-min.json',
	beforeSend: function(xhr){
		if (xhr.overrideMimeType) {
			xhr.overrideMimeType("application/json");
		}
	},
	success: function(data) {
		var allotments = data.allotments;

		for (var i = 0; i < allotments.length; i++) {
			createMarker(allotments[i]);
		}

		map.fitBounds(bounds);
	}
});

Then I simply add the markers, setup an event listener to update the infowindow with the relevant content, and extend the map’s bounds to fit them all in:

function createMarker(allotment) {
	var marker = new google.maps.Marker({
		position: {lat: allotment.Latitude, lng: allotment.Longitude},
		map: map,
		title: allotment.Name
	});

	bounds.extend(marker.getPosition());

	var content = '<strong>' + allotment.Name + '</strong><br>';
	if (allotment.Location) {
		content += 'Location: ' + allotment.Location + '<br>';
	}
	if (allotment.Borough) {
		content += 'Borough: ' + allotment.Borough + '<br>';
	}
	if (allotment.Organisation) {
		content += 'Organisation: ' + allotment.Organisation + '<br>';
	}
	if (allotment.Facilities) {
		content += 'Facilities: ' + allotment.Facilities + '<br>';
	}
	if (allotment.Comments) {
		content += 'Comments: ' + allotment.Comments + '<br>';
	}

	marker.addListener('click', function() {
		infowindow.setContent(content);
		infowindow.open(map, this);
	});
}

And this produces a map that looks like this:

Allotments_-_2016-06-13_21.40.48

Well that works, and it’s simple, but there’s really too many markers tightly grouped together, and you can’t filter them down by Borough for instance.  What can we do to improve this?  The Google Maps API documentation lists several things you can do when working with large datasets.  Let’s try a KML Layer.  I need to convert my data to a KML format for starters.  I used this handy site to do that for me, turning my 59Kb .csv file into a 466Kb .kml file.  Each allotment now turned into an XML structure like this:

<Placemark>
	<name>Abbots Way</name>
	<ExtendedData>
		<SchemaData schemaUrl="#csv_20160528085652">
			<SimpleData name="Name">Abbots Way</SimpleData>
			<SimpleData name="Location">Alongside railway line</SimpleData>
			<SimpleData name="Borough">Bromley</SimpleData>
			<SimpleData name="Organisation"></SimpleData>
			<SimpleData name="Facilities"></SimpleData>
			<SimpleData name="Comments"></SimpleData>
			<SimpleData name="Latitude">51.393386</SimpleData>
			<SimpleData name="Longitude">-0.047422</SimpleData>
		</SchemaData>
	</ExtendedData>
	<Point>
		<coordinates>-0.047422,51.393386</coordinates>
	</Point>
</Placemark>

Great! So according to Google’s docs, it’s simply a case of adding a KmlLayer like so:

var kmlLayer = new google.maps.KmlLayer({
	url: 'http://www.example.com/allotments.kml',
	map: map
});

Firstly the KML file has to be publicly accessible, so I had it uploaded to my server (I’m only running the HTML file locally on my laptop).  However it didn’t like that; I had to add KML (application/vnd.google-earth.kml+xml) to my list of mime types in IIS, otherwise I got a 404 error.

This still didn’t seem to work, and I stumbled across something which suggested any KML file over 10Kb should really be turned into a KMZ file instead.  That was simply a case of zipping up the KML file, and changing the file extension to .kmz.  Oh, and then adding KMZ (application/vnd.google-earth.kmz) as a mime type in IIS as well.

This still didn’t give me my full results; markers appear, but clicking each one just gave me the title, none of the other data.  Turns out the lovely KML format I was working with contained lots of elements Google Maps API aren’t supporting (here’s the full list of what they do).  So I had to reformat it, mainly replacing all the SimpleData elements with Data elements instead, and getting rid of a Schema declaration at the top, and ending up having each allotment in this format:

<Placemark>
	<name>Abbots Way</name>
	<ExtendedData>
		<Data name="Name"><value>Abbots Way</value></Data>
		<Data name="Location"><value>Alongside railway line</value></Data>
		<Data name="Borough"><value>Bromley</value></Data>
		<Data name="Latitude"><value>51.393386</value></Data>
		<Data name="Longitude"><value>-0.047422</value></Data>
	</ExtendedData>
	<Point><coordinates>-0.047422,51.393386</coordinates></Point>
</Placemark>

Finally that started working, giving me this kind of result…

Allotments_KML_-_2016-06-13_21.44.06

At this point there’s steps I could take to tidy up the layout of the infoWindow, but why bother?  End result: a lot of faffing around for not much different from before.  In retrospect, it seems KML is really a format more for the benefit of Google Earth than Google Maps, and I’m not sure I came up with anything useful just by changing my code to use that file format.  Other than learning what’s needed to use KML files with the Google Maps API for future reference.

What about applying some marker clustering just to reduce the huge number of markers?  That’s not too tricky, I just set everything up like in my original example, included the MarkerCluster JS file, and added this line in after I’d put all the markers into an array:

var markerCluster = new MarkerClusterer(map, markers, {imagePath: 'markerclusterer/images/m'});

The only gotcha I had was I needed to add the imagePath for the marker images to appear correctly (you may not need this, depending where your JS file is).  And this then gave me:

Allotments_-_2016-06-13_21.49.14

Zooming in a bit you start to see different icons and individual markers, e.g.

Allotments_-_2016-06-13_21.55.33

Well a bit better for reducing the amount of markers displayed at any one time, and giving you an idea of how they’re grouped across the city, but still far from ideal.  What I really want is to break it down by the various boroughs.

Next step, FusionTables.  This article by Dan Nguyen was very useful: Intro to Data Mashing and Mapping with Google Fusion Tables.  It took a bit of trial-and-error, importing data from Google Spreadsheets to Google FusionTables.  I ended up with one file in Google Spreadsheets. It contained two spreadsheets:

One with all 741  allotments and their coordinates:

The other with all 33 boroughs and the coordinates for the polygons defining their boundaries:

This data came from this publicly-available KML file; I’m not sure how accurate or up-to-date that is, and it may not tally with the data to do with the allotments (e.g. boundary changes since that KML file was made may put some allotments in the wrong councils on the map).  In this second sheet I added a new column, for a count of the allotments per borough.  This used a simple formula, using COUNTIF to tally up how often the name of each council appears in the other sheet:

=COUNTIF(Allotments!C2:C742,A2)

You’ll notice in the above screenshot that in the row for Bromley the ‘geometry’ column is blank; this was also the case for several of the other councils.  It seemed to be a problem importing from the KML file into Google Spreadsheets.  I think I ended up turning this into a FusionTable, then manually editing the values for any missing polygons.

So at this point I imported both the spreadsheets into FusionTables as separate tables.  In FusionTables you get an option to turn your table into a map.  You can then choose Publish > Get HTML and JavaScript, and get all the code you’d need to turn that into a web page.  Doing that with each of these tables, I got two separate maps;

One with all the council boundaries:

Merge_of_London_Allotments_by_Borough_-_Google_Fusion_Tables_-_2016-06-14_23.06.45

And the other with small markers for all the allotments:

London_Allotments_by_Borough_-_Google_Fusion_Tables_-_2016-06-14_23.05.24

In Fusion Tables it was easy to setup the colour schemes and add the ‘# of allotments‘ legend.  All I need to do now is combine both of these into one map.

So initially I want to just display the council boundaries, so this FusionTablesLayer does that:

var boroughsLayer = new google.maps.FusionTablesLayer({
    map: map,
    suppressInfoWindows: true,
    query: {
        select: 'geometry',
        from: '15nhaHjAOYp2CrBJRJoP5bXkytmgfuRXYvGwsIuIk'
    },
    styles: [{
        where: 'count = 0',
        polygonOptions: {
            strokeColor: '#000000',
            strokeOpacity: 0.3,
            strokeWeight: 1,
            fillColor: '#edf8e9',
            fillOpacity: 0.1
        }
    },{
        where: 'count > 0',
        polygonOptions: {
            strokeColor: '#000000',
            strokeOpacity: 0.3,
            strokeWeight: 1,
            fillOpacity: 0.5,
            fillColor: '#bae4b3'
        }
    },{
        where: 'count > 15',
        polygonOptions: {
            strokeColor: '#000000',
            strokeOpacity: 0.3,
            strokeWeight: 1,
            fillOpacity: 0.5,
            fillColor: '#74c476'
        }
    },{
        where: 'count > 30',
        polygonOptions: {
            strokeColor: '#000000',
            strokeOpacity: 0.3,
            strokeWeight: 1,
            fillOpacity: 0.5,
            fillColor: '#31a354'
        }
    },{
        where: 'count > 45',
        polygonOptions: {
            strokeColor: '#000000',
            strokeOpacity: 0.3,
            strokeWeight: 1,
            fillOpacity: 0.5,
            fillColor: '#006d2c'
        }
    }]
});

The query gets all the polygon data.  Then we want to give each polygon a different style based on the number of allotments.  If you omit the ‘where’ part, you can set a default style; however you can only set up to 5 of these styles, and because I’m wanting five different colours based on the allotment count, I need to just specify all the styles for each possible option (and so I end up repeating all the properties apart from the fillColors).

This is the HTML for the map and legend:

<div id="map"></div>
    
<div id="legend">
    <p id="legend-title"># of allotments</p>
    <div>
        <span class="legend-swatch" style="background-color: #edf8e9"></span>
        <span class="legend-range">0</span>
    </div>
    <div>
        <span class="legend-swatch" style="background-color: #bae4b3"></span>
        <span class="legend-range">1 - 15</span>
    </div>
    <div>
        <span class="legend-swatch" style="background-color: #74c476"></span>
        <span class="legend-range">16 - 30</span>
    </div>
    <div>
        <span class="legend-swatch" style="background-color: #31a354"></span>
        <span class="legend-range">31 - 45</span>
    </div>
    <div>
        <span class="legend-swatch" style="background-color: #006d2c"></span>
        <span class="legend-range">46+</span>
    </div>
</div>

The values for the fillColor property obviously match up with the background colours on the legend. This adds the legend onto the map:

map.controls[google.maps.ControlPosition.RIGHT_TOP].push(document.getElementById('legend'));

Now what I want is if you click on any of the councils, it shows you just the allotments there.  This does that:

allotmentsLayer = new google.maps.FusionTablesLayer();

boroughsLayer.addListener('click', function(FusionTablesMouseEvent) {
    allotmentsLayer.setMap(null);
    
    allotmentsLayer.setOptions({
        map: map,
        query: {
            select: 'col6',
            from: '1kBhYAiZGBsIzZ-iXQZ0VC8Lhr32IUf_WOp-cYntm',
            where: "'Borough' = '" + FusionTablesMouseEvent.row.name.value + "'"
        },
        options: {
            styleId: 2,
            templateId: 2
        }
    });
});

So firstly I’ve got a global variable for the allotmentsLayer.  Each time I click a new council, I set its map property to null, removing any markers that were previously visible.  The FusionTablesLayer‘s click event handler gives you a FusionTablesMouseEvent, which lets you know which row in the FusionTable that equates to.  From this, I can get the name of the council, and I can then use that to query the FusionTable with all the allotments.

I’m also specifying an options property on the layer here.  This isn’t documented in the Maps API, but when you get the generated HTML + Javascript from FusionTables, it includes those depending on how you style your markers.  And they seemed to be required; I wasn’t able to style my markers otherwise from what I could see.

What I’m also doing is outputting the name of the council and the number of allotments it contains.  And providing a ‘show all‘ link so you can see all the allotments at any time.

<div id="borough">
    <strong id="name"></strong> <span id="count"></span>
    <p><a href="" id="showAll">Show all allotments</a></p>
</div>

I wrap these up in a div and treat it like the legend, and add it directly onto the top-middle of the map:

map.controls[google.maps.ControlPosition.TOP_CENTER].push(document.getElementById('borough'));

And in the click event listener, I update the value of the HTML:

$('#name').text(FusionTablesMouseEvent.row.name.value + ': ');
$('#count').text(FusionTablesMouseEvent.row.count.value + ' allotments');
$('#showAll,#borough').show();

The ‘show all allotments‘ link has its own event listener, which just does the query again, but without a ‘where’ clause:

$(document).ready(function() {
    $('#showAll').on('click', showAllAllotments);        
});

function showAllAllotments(event) {
    event.preventDefault();
    $('#showAll,#borough').hide();
    
    allotmentsLayer.setMap(null);
        
    allotmentsLayer.setOptions({
        map: map,
        query: {
            select: 'col6',
            from: '1kBhYAiZGBsIzZ-iXQZ0VC8Lhr32IUf_WOp-cYntm'
        },
        options: {
            styleId: 2,
            templateId: 2
        }
    });
}

And what all this gives is this:

Allotments_-_Fusion_Table_-_2016-06-15_20.24.19

You can see it working here.  This is more or less what I was hoping to end up with.  I would have liked to fit the bounds of the map to fit each council as it was selected.  And to have a list of all the councils, perhaps as a dropdown you could choose from.  I’m sure  these things must be possible.

From a data point of view, it would be good to include things like the length of the allotment waiting lists, or to have slightly more useful data about each allotment than just what the DataStore provided.

Next steps:

  • use a different mapping system such as CartoDB or MapBox
  • map what tree data is available for London

Some useful resources:

October 16, 2015

Google Maps – editable polylines

Filed under: Google Maps,Javascript — duncan @ 12:01 am
Tags: , ,

In Google Maps Javascript API v3, it’s possible to make polylines editable.  But it’s not immediately obvious what you can do with that.  In this post I try and explore some of what you can then do.

Firstly, it’s very simple to create a polyline.  According to the documentation it requires a path as part of the PolylineOptions used in its constructor.  And that path can either be a normal javascript array or Google’s own MVCArray – this distinction will come in useful later on – or even just a single LatLngLiteral.

The LatLngLiteral is where instead of constructing a point like:

x = new google.maps.LatLng(51.5286416, -0.1015987)

you can simply use:

x = {lat: 51.5286416, lng: -0.1015987}

So here’s a simple example.  In this case I’m just going to start by trying to  use a javascript array of LatLngLiterals:

var path = [
	{lat: 51.5573205, lng: -0.1663994},
	{lat: 51.5636304, lng: -0.1613568}
];

var polyline = new google.maps.Polyline({
	path: path,
	map: map
	editable: true
});

By adding that editable: true option, we go from this to this:

Map_with_editable_polyline_-_2015-10-15_22.52.59 Map_with_editable_polyline_-_2015-10-15_22.58.59

We now have a polyline with the two end vertices we can now drag.  There’s also a placeholder vertex at the centre, and dragging that adds it as a new point to the path.

Next, what we can do is add event listeners.  Google’s documentation has a little bit of useful information about editable shapes and their event listeners.

The Polyline class has several events you can listen for, including drag, dragstart and dragend.  But it’s not got any that seem to be related to when the polyline is being edited (i.e. individual points being dragged, rather than the whole line).

Instead, we need to have event listeners for the path which we’ve just constructed the polyline with.  Again reading the documentation, the MVCArray class has event listeners we can use, insert_at, set_at and remove_at. The first two happen automatically as we drag the polyline’s points; the remove_at one we can trigger by calling the removeAt function on the path (more on that later):

google.maps.event.addListener(path, 'insert_at', function(vertex) {
	console.log('Vertex ' + vertex + ' inserted to path.');
});

google.maps.event.addListener(path, 'set_at', function(vertex) {
	console.log('Vertex ' + vertex + ' updated on path.');
});

However, nothing happens using all this code.  We get an editable polyline, but the event listeners don’t seem to trigger when I drag the individual points on it.  The documentation says “Note that if you pass a simple array, it will be converted to an MVCArray“.  So you’d assume this was the case here, and I could then expect the MVCArray events to be triggered, but it doesn’t seem to be the case.

So, despite being able to construct a polyline with a single LatLngLiteral, passing an array of them prevents any of the path listener events triggering.

Instead we need to amend how we define the path; it needs to be explicitly an MVCArray, not just a normal javascript array.  You’d think we could just do:

var path = new google.maps.MVCArray([
	{lat: 51.5573205, lng: -0.1663994},
	{lat: 51.5636304, lng: -0.1613568}
]);

However that throws a javascript error, and no line appears:

InvalidValueError: at index 0: not an instance of LatLng

So third time lucky; we have to define the path as an MVCArray of LatLng objects, not an MVCArray of LatLngLiterals, or a normal javascript array of either LatLngs or LatLngLiterals.

var path = new google.maps.MVCArray([
	new google.maps.LatLng(51.5573205, -0.1663994),
	new google.maps.LatLng(51.5636304, -0.1613568)
]);

Ok, finally, this works.  Now what… well in our event listener functions, the only argument passed in is an integer indicating which vertex was added / edited / removed.  Great.  We can also access the path itself, of course.  And from that we could find out the coordinates of all the vertices, and the path’s length, which might be useful.

So I’m going to use a little bit of jQuery to output that information.  I call this function from each of the event listeners, so we always have an up-to-date list of coordinates and length of the path:

function updateCoords(path) {
	var row;
	$('#length').text('Length: ' + google.maps.geometry.spherical.computeLength(path).toFixed(2));
	$('#vertices tr:gt(0)').remove();
	path.forEach(function(element, index) {
		row = $('<tr>');
		row.append('<td>' + (index+1) + '</td>');
		row.append('<td>' + element.lat() + '</td>');
		row.append('<td>' + element.lng() + '</td>');
		row.append('<td><a href="">X</a></td>');
		$('#vertices').append(row);
	});
}

To get the length, I’m using Google’s Geometry library.  To use this, you have to include it as a parameter when you load in the Maps API:

https://maps.googleapis.com/maps/api/js?v=3&libraries=geometry

And what we end up with is something like this:

Map_with_editable_polyline_-_2015-10-15_23.31.23

As well as displaying the coordinates for each vertex, I’ve also added a link to remove each one, and this jQuery event listener for then removing them from the path. I didn’t want to completely remove the path, so make sure there’s always at least 2 points:

$('body').on('click', 'a', function(event) {
	event.preventDefault();
	if (path.getLength() > 2) {
		path.removeAt($(this).data('id'));
	}
	return false;
});

In a typical application you’d probably want to do something like fire off an AJAX request to store the coordinates for use later on, or something like that.  This was just a simple example to see how you needed to construct the polyline in order to use its event listeners.  You can see it in action, and the full code, here.

Update: Havelly asked how we could make a right-click on the line do the same as clicking the X in the table, to remove a section of the path.  As I mentioned in the comments, the Polyline has a ‘rightclick’ event listener, which gives you access to a PolyMouseEvent. The PolyMouseEvent has properties for edge, vertex and path, which you’d think would do the trick. However I couldn’t seem to ever get the path property; I suspect it’s only available on Polygons, not Polylines.

Instead, the PolyMouseEvent also gives you the latLng of where the event occurred, which we can use instead. I ended up drawing an invisible Polyline for each section of the Path.  In response to the rightclick event on the original Polyline, I loop over that array of hidden polylines, checking if the latLng where we just right-clicked is on that individual polyline, using the google.maps.geometry.poly::isLocationOnEdge function.  And if it is, we can them just call the remoteAt function.

This code’s slightly rough and untested, but seemed to work.  I had to tweak the tolerance a bit until I got it right; possibly you’d need to adjust it to something else depending on stroke width and so on.

To make this work, I made the map variable global, and also added one for an array of polylines:

var map, polylines;

When I create the polyline, I also add an event listener for the right-click:

polyline.addListener('rightclick', function(polyMouseEvent) {
	for (var i = 0; i < polylines.length; i++) {
		if (google.maps.geometry.poly.isLocationOnEdge(polyMouseEvent.latLng, polylines[i], 0.0001)) {
			path.removeAt(i);
		}
	}
});

That polylines array I’m looping over, I create inside the updateCoords function.  Firstly inside that function, I reset polylines to an empty array:

polylines = [];

Then inside the foreach that I’ve already got in place to loop over all the vertices in the MVCArray path, I create an invisible polyline and add it into the array of polylines.  I only do this if we’re past the first of the vertices.  And I then save these coordinates in the variable point, so I can then use that in the next iteration of this forEach loop, for an endpoint of the next invisible polyline.

if (index > 0) {
	polyline = new google.maps.Polyline({
		path: [
			point,
			element
		],
		map: map,
		visible: false,
		geodesic: true
	});
	
	polylines.push(polyline);
}

point = element;

And that’s it!

March 21, 2015

Google Maps API – locked draggable markers

Filed under: Google Maps,Javascript — duncan @ 1:53 pm
Tags: , , ,

This is a nice simple post, inspired by a recent question on StackOverflow:

How to check if marker is out of specific constant bounds?

In summary, the user had defined a bounds, and wanted users to be able to drag a marker, but not outside of those bounds.

So firstly, you can simply use the LatLngBounds contains’ function to determine if a point is within those bounds.  And you can have an event listener for when the user drags the marker.  Usually you’d just want to check for the dragend event, when they stop dragging.

In this example I’ve drawn a semi-transparent rectangle using the bounds, so it’s obvious the area where you can drag your marker in.

I also keep track of the coordinates the marker had when they start dragging it.  If they go outside of the bounds, I reset it to that position.

Initial state of the map:

bounds map1

And after the user drags the marker outside of the bounds:

bounds map2

Here’s the code:

<!DOCTYPE html>
<html>
<head>
	<title>Locked draggable markers</title>
	<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
	<meta charset="utf-8">
	<style>
		#map {
			width: 640px; height: 480px;
		}
	</style>
	<script src="https://maps.googleapis.com/maps/api/js?v=3"></script>
	<script>
		function initialize() {
			var bounds = new google.maps.LatLngBounds(
				new google.maps.LatLng(51.5089872,-0.155157), 
				new google.maps.LatLng(51.599285,0.032984)
			);
			
			var map = new google.maps.Map(document.getElementById("map"), {
			  center: bounds.getCenter(),
			  zoom: 13,
			  mapTypeId: google.maps.MapTypeId.ROADMAP
			});
			
			map.fitBounds(bounds);
			
			var rectangle = new google.maps.Rectangle({
				bounds: bounds,
				map: map,
				fillOpacity: 0.2,
				fillColor: 'blue',
				strokeOpacity: 0
			});
					 
			var marker=new google.maps.Marker({
				map: map,
				position: bounds.getCenter(),
				draggable: true
			});
				
			var markerPosition;

			google.maps.event.addListener(marker, 'dragstart', function() {
				markerPosition = this.getPosition();
			});

			google.maps.event.addListener(marker, 'dragend', function() {
				if (bounds.contains(this.getPosition()) == false) {
					alert("You've dragged the marker outside of the bounds allowed. We've reset it");
					
					this.setPosition(markerPosition);
				}
			});
		}
		
		google.maps.event.addDomListener(window, 'load', initialize);
	</script>
</head>
<body>
	<div id="map"></div>
</body>
</html>

You’ll noticed I’m only declaring coordinates for the Bounds, and then getting the Bounds’ centre, and using that to plot the centre of the map and the initial coordinates for the marker.  And then making the map fit the bounds object.

One interesting note; The Google Maps API has started accepting coordinates in a lot of its classes in this shorthand format:

 {lat: x, lng: y}

… instead of having to create a new LatLng object every time:

 new google.maps.LatLng(x, y)

However, this doesn’t seem to work (yet) with the Bound class.

 

Secondly, as well as a bounds object, it might be useful to do the same thing on a polygon that you’ve drawn on the map.  This is slightly more complicated; the google.maps.Polygon class hasn’t got a contains event.  However the Geometry library does.  You need to load it in when you specify the Maps API URL:

<script src="https://maps.googleapis.com/maps/api/js?v=3&libraries=geometry"></script>

Here I’ve just plotted a polygon roughly corresponding to the border of Utah:

Locked_draggable_markers_-_2015-03-21_13.34.50

I construct that initially as an array of LatLng points.

var boundary = [
    new google.maps.LatLng(41.987058, -114.007506),
    new google.maps.LatLng(41.987058, -111.063170),
    new google.maps.LatLng(40.999595, -111.063170),
    new google.maps.LatLng(40.999595, -109.041686),
    new google.maps.LatLng(37.005844, -109.063658),
    new google.maps.LatLng(37.005844, -114.051451)
];

I then loop over the array, adding each coordinate to a bounds object.

var bounds = new google.maps.LatLngBounds();

for(var i = 0; i < boundary.length; i++) {
    bounds.extend(boundary[i]);
}

Then I use the array of coordinates (not the bounds), to plot the path for a polygon object.

var polygon = new google.maps.Polygon({
    paths: boundary,
    map: map,
    fillOpacity: 0.2,
    fillColor: 'blue',
    strokeOpacity: 0
});

And finally just a slight change to the event listener, to check if the polygon contains the marker’s position.

google.maps.event.addListener(marker, 'dragend', function() {
    if (google.maps.geometry.poly.containsLocation(this.getPosition(), polygon) == false) {
        alert("You've dragged the marker outside of the bounds allowed. We've reset it");
            
        this.setPosition(markerPosition);
    }
});

Nice and simple!

January 22, 2015

Animated paths with Google Maps

Filed under: Google Maps,Javascript — duncan @ 6:39 pm
Tags: , ,

I saw this question on StackOverflow a few days ago.  There was a map with animated polylines being drawn along a set of coordinates, with a minor bug in it.  The very next day I saw another question asking how to animate a route on a map.  I thought the approach I’d seen the day before was ideal, and posted a slightly re-written version of it as an answer. I thought there was enough merit in the technique used to turn it into a blog post.  Thanks to the OP Alex Man for the original version of the code.

Version 1

Animated_route_-_2015-01-22_18.35.41

function initialize() {
	var map = new google.maps.Map(document.getElementById("map"), {
	  center: {lat: pathCoords[0].lat, lng: pathCoords[0].lng},
	  zoom: 11,
	  mapTypeId: google.maps.MapTypeId.ROADMAP
	});
	
	autoRefresh(map);
}

function moveMarker(map, marker, latlng) {
	marker.setPosition(latlng);
	map.panTo(latlng);
}

function autoRefresh(map) {
	var i, route, marker;
	
	route = new google.maps.Polyline({
		path: [],
		geodesic : true,
		strokeColor: '#FF0000',
		strokeOpacity: 1.0,
		strokeWeight: 2,
		editable: false,
		map:map
	});
	
	marker=new google.maps.Marker({map:map,icon:"http://maps.google.com/mapfiles/ms/micons/blue.png"});

	for (i = 0; i < pathCoords.length; i++) {
		setTimeout(function (coords)
		{
			var latlng = new google.maps.LatLng(coords.lat, coords.lng);
			route.getPath().push(latlng);
			moveMarker(map, marker, latlng);
		}, 200 * i, pathCoords[i]);
	}
}

google.maps.event.addDomListener(window, 'load', initialize);

var pathCoords = [
	{
	"lat": 8.893260000000001,
	"lng": 76.61427
	},
	{
	"lat": 8.894430000000002,
	"lng": 76.61418
	},
	// etc for hundreds more coordinates

View full code

The interesting bit here is the ‘autoRefresh‘ function.  That creates an empty polyline, then loops over the global pathCoords array.  For each set of coordinates, it creates a call to this anonymous function, at 200 millisecond intervals:

function (coords) {
	var latlng = new google.maps.LatLng(coords.lat, coords.lng);
	route.getPath().push(latlng);
	moveMarker(map, marker, latlng);
}

So every 200 milliseconds it calls that function.  When using setTimeout I pass in a third parameter, which is a single set of coordinates.  I’m not sure this is an ideal approach, for instance if something in that function took longer than 200ms to execute, things could get a bit disjointed looking.  But it seems to work for now.

For each set of coordinates, it adds it to the current polyline’s path.  You might have thought I’d have to use setPath, but just pushing it onto the array that getPath returns works.

Then I update the position of the marker to the current point, and pan the map.

One thing worth noting: Google Maps have started accepting coordinates in this format in a lot of places:

	{lat: 1.234, lng: 2.345}

instead of having to always create new LatLng objects each time:

	new google.maps.LatLng(1.234, 2.345)

In the example above, the coords object was fine when passed straight to the moveMarker function, i.e. for setting the marker and panning the map.  However it didn’t work when pushing it into the array of the polyline’s path.  So I still had to create a LatLng object for that.  Possibly if I’d saved the polyline path from getPath into a variable, appended the coords parameter, then done setPath with the full array, it would have been fine.

See the map working here

Version 2

Animated_route_-_2015-01-22_18.36.08

Instead of having to hardcode hundreds of coordinates in our javascript, we can use the DirectionsService, simply specifying a start and end point.  The data it returns includes a list of all the coordinates we’d need.  Then instead of rendering the directions, we can simply use those coordinates to draw our own animated path.

function initialize() {
    var map = new google.maps.Map(document.getElementById("map"), {
      center: {lat: 51.5087531, lng: -0.1281153},
      zoom: 7,
      mapTypeId: google.maps.MapTypeId.ROADMAP
    });
    
    getDirections(map);
}

function moveMarker(map, marker, latlng) {
    marker.setPosition(latlng);
    map.panTo(latlng);
}

function autoRefresh(map, pathCoords) {
    var i, route, marker;
    
    route = new google.maps.Polyline({
        path: [],
        geodesic : true,
        strokeColor: '#FF0000',
        strokeOpacity: 1.0,
        strokeWeight: 2,
        editable: false,
        map:map
    });
    
    marker=new google.maps.Marker({map:map, icon:"http://maps.google.com/mapfiles/ms/micons/blue.png"});

    for (i = 0; i < pathCoords.length; i++) {                
        setTimeout(function(coords) {
            route.getPath().push(coords);
            moveMarker(map, marker, coords);
        }, 200 * i, pathCoords[i]);
    }
}

function getDirections(map) {
    var directionsService = new google.maps.DirectionsService();

    var request = {
        origin: new google.maps.LatLng(51.5087531, -0.1281153),
        destination: new google.maps.LatLng(48.8583694, 2.2944796),
        travelMode: google.maps.TravelMode.DRIVING
    };
    directionsService.route(request, function(result, status) {
        if (status == google.maps.DirectionsStatus.OK) {
            autoRefresh(map, result.routes[0].overview_path);
        }
    });
}

google.maps.event.addDomListener(window, 'load', initialize);

See the second map working here

Again, I wasn’t able to just use a coords struct, I had to create LatLng objects for passing into the DirectionsService request. The directions service happens asynchronously, so I have to wait for when it returns back an ‘OK’ status before creating the path.

Apart from replacing the array of coordinates with the value returned by the DirectionsService’s overview_path property, the code for drawing the path is basically the same as before.

Further ideas:

  • Instead of just creating a polyline from all the coordinates in the overview_path, we could loop over all the legs of the route.  This would give us more flexibility to amend the polyline, e.g. you could examine the duration property of each DirectionsStep and change the colour of the polyline.
  • We could use the bounds property of the DirectionsRoute to update the map’s bounds so the entire route is always visible.  Doing this you wouldn’t need to constantly pan the map centre.

January 5, 2015

Bristol coffee shops

Filed under: Coffee — duncan @ 8:00 am
Tags: , , ,

After a recent trip to Bristol, in keeping with previous articles on coffee shops in Dublin, New York, Zagreb and  New Zealand and Australia, I thought I’d do a write-up for some cafés we went to.

Playground Coffee House

45 St. Nicholas Street

The first place we called at was the Playground Coffee House.  It hadn’t opened for the morning, we were slightly too early, but we were still able to get some very nice coffees.  It looked like a fun place to go; from the usual like board games, to the unusual like, er, swings for seats.  Which come with their own warning, they maybe don’t mix too well with hot drinks!

The barista was very friendly, and recommended to us a few of the other best coffee shops in town.

P1010287 P1010288 P1010289 P1010291 P1010293

Facebook / Twitter

Reviews:

Bear Pit Social

St. James Barton Underpass

This place is housed in a converted shipping container, in the, er, interesting location of the Bearpit, aka the St. James Barton Roundabout underpass… not as bad as it sounds.  In fact they had quite a nice little seating area in one corner, and they were doing food in addition to the coffees.

P1010303

Facebook / Twitter

Reviews:

Spicer and Cole

1 Queen Square Avenue

Spicer and Cole was listed on our map, out in Clifton.  We didn’t go there, but they also had a branch in the centre, on Welsh Back, very close to Queen Square.  I overheard someone in the street raving about it to a couple who were looking for somewhere to eat.  I went there a couple of times; the coffee was ok; we had some nice Sunday breakfast.

P1010463 P1010465 P1010466

Website / Facebook / Twitter

Review:

Full Court Press – Speciality Coffee

59 Broad Street

Full Court Press I visited a couple of times.  The name’s apparently a basketball reference.  They seemed to take their coffee pretty seriously (as their sub-title would imply), and both times the baristas gave me good descriptions of the choice of beans available.

P1010434 P1010435 P1010585 P1010586

WebsiteFacebook / Twitter / Pinterest

Reviews:


I intended to visit some of these too, but unfortunately they were all closed on the Sunday:

  • Didn’t You Do Well
  • Workhouse Cafe
  • Small Street Espresso

The coffee map I bought in Playground was very useful; I’d recommend it if you were on a trip to Bristol and wanted to visit a few different cafés.  It listed 24 different places to go, with lots of information and an attractively-designed map.

Website / Twitter

(when I first checked the website was fine; but it seems be offline right now).

January 1, 2015

2014 in review

Filed under: Uncategorized — duncan @ 7:00 am

The WordPress.com stats helper monkeys prepared a 2014 annual report for this blog.

Here’s an excerpt:

The concert hall at the Sydney Opera House holds 2,700 people. This blog was viewed about 37,000 times in 2014. If it were a concert at Sydney Opera House, it would take about 14 sold-out performances for that many people to see it.

Click here to see the complete report.

December 18, 2014

Project Euler: problem 46 – Goldbach’s other conjecture

Filed under: PHP,Project Euler — duncan @ 11:57 pm
Tags: , , ,

46I’m doing these Project Euler mathematical puzzles as a simple practical exercise for teaching myself PHP, and I’d appreciate any feedback on my code

Problem 46:

It was proposed by Christian Goldbach that every odd composite number can be written as the sum of a prime and twice a square.

9 = 7 + 2×12
15 = 7 + 2×22
21 = 3 + 2×32
25 = 7 + 2×32
27 = 19 + 2×22
33 = 31 + 2×12

It turns out that the conjecture was false.

What is the smallest odd composite that cannot be written as the sum of a prime and twice a square?

This was another of those problems that I’d initially passed over, having decided it looked a bit tricky.  Then looking at it again realised it probably wasn’t too difficult.  And I turned out to be right!

A composite number is basically any positive integer that isn’t a prime number.

My logic is simply:

  • Loop through odd numbers, from 3 upwards.
  • For each number, if it’s prime, add it to an array of primes for later reference.
  • If it’s not prime, work out if it matches the conjecture:
    • Subtract the next largest prime, then examine the remainder
    • Divide the remainder by 2.  if it’s a square number then we’re meeting the conjecture.  Move onto the next odd number.
    • Otherwise keep subtracting the primes, checking the remainders.
    • If we’ve looped through all the primes then we must have reached a number that doesn’t meet the conjecture.

This runs in about 40ms:

<?php
$primes = [2];

$i= 1;

while (true) {
	$i+= 2;

	if (isPrime($i)) {
		$primes[] = $i;
	} else {
		for($j = count($primes)-1; $j > 0; $j--) {
			$remainder  = $i - $primes[$j];
			$remainder = $remainder / 2;
			$root = sqrt($remainder);
			if ((int) $root == $root) {
				continue 2;
			}
		}
		
		echo $i;
		break;
	}
}


function isPrime($x)
{
	$root = sqrt($x);
	
	for ($i = 3; $i <= $root; $i += 2) {
		if ($x % $i == 0) {
			return false;
		}
	}
	
	return true;
}

I’m using a modified version of my original isPrime function, just because I know I don’t need to check for even numbers.

The loop backwards through the primes was maybe a bit of overkill; using a simple foreach loop through the primes in ascending order took more like 100ms.

What else… I check if a square root is the same as when it’s cast to an integer (not sure this is the best approach).  Then use continue 2; to get out of our inner-most loop and move onto the next value in our parent loop.

December 14, 2014

Project Euler: problem 33 (PHP) – Digit cancelling fractions

Filed under: PHP,Project Euler — duncan @ 10:54 pm
Tags: ,
Fractions

Picture by jessicakelly

I’m doing these Project Euler mathematical puzzles as a simple practical exercise for teaching myself PHP, and I’d appreciate any feedback on my code

Problem 33:

The fraction 49/98 is a curious fraction, as an inexperienced mathematician in attempting to simplify it may incorrectly believe that 49/98 = 4/8, which is correct, is obtained by cancelling the 9s.

We shall consider fractions like, 30/50 = 3/5, to be trivial examples.

There are exactly four non-trivial examples of this type of fraction, less than one in value, and containing two digits in the numerator and denominator.

If the product of these four fractions is given in its lowest common terms, find the value of the denominator.

It took me a couple of tries to get this, I think as the question doesn’t give enough details about what the rules are for anomalous fraction cancellation.

Supposing we say our fractions are of the form ab / cd.  Initially I was checking each of the following:

  • a / c = ab / cd
  • a / d = ab / cd
  • b / c = ab / cd
  • b / d = ab / cd

i.e. each possible combination of the first and second numerator and denominator digits.

Then I realised I could omit two of these, as we didn’t want to look at those cases where it’s the first numerator digit divided by the first denominator digit, or the second numerator digit divided by the second denominator digit.  Reducing what I was checking down to:

  • a / d = ab / cd
  • b / c = ab / cd

So I was now just looking at the first numerator over the second denominator, and the second numerator over the first denominator.  However this was still giving me too much possible fractions.  Further reading illustrated that the example given, 49 / 98, where the second numerator digit and the first denomator digit are cancelled out, is the rule for all cases.  Reducing what I was checking to just:

  • a / d = ab / cd

The question also doesn’t really explain what else you can ignore.  Firstly if either digit is zero.  Secondly, the trivial examples include where a = b and c = d, e.g. 11 / 22.

And finally, I thought you could look at any values for ab and cd, e.g. I’d got 16 / 96 = 1 / 6.  But this meant I’d still got more than four ‘matching’ fractions.  What the question failed to mention is that the digits being cancelled out had to be identical, so really what I was checking was changed to:

  • a / c = ab / bc

Once I’d got all that cleared up, it was easy.

<?php
$product = 1;

for ($numerator = 10; $numerator <= 99; $numerator++) {
	for ($denominator = $numerator+1; $denominator <= 99; $denominator++) {
		$fraction = $numerator / $denominator;
		
		$numeratorAsString = (string)$numerator;
		$denominatorAsString = (string)$denominator;
		
		if (
			!isTrivial($numeratorAsString, $denominatorAsString) && 
			$numeratorAsString[1] == $denominatorAsString[0] && 
			$numeratorAsString[0] / $denominatorAsString[1] == $fraction
		) {
			$product *= $fraction;
		}
	}
}

function isTrivial($numerator, $denominator) {
	if ($denominator[1] == 0) {
		return true;
	}
	
	if ($numerator[0] == $numerator[1]) {
		return true;
	}
	
	return false;
}

echo $product;

I cast my numerators and denominators to strings, enabling me to then reference the digits within each using array notation.

Some useful links:

December 10, 2014

Project Euler: problem 27 (PHP) – Quadratic primes

Filed under: PHP,Project Euler — duncan @ 8:00 am
Tags: , ,
Ploo!

Photo by Ianqui Doodle

I’m doing these Project Euler mathematical puzzles as a simple practical exercise for teaching myself PHP, and I’d appreciate any feedback on my code.

Problem 27:

Euler discovered the remarkable quadratic formula:

n² + n + 41

It turns out that the formula will produce 40 primes for the consecutive values n = 0 to 39. However, when n = 40, 402 + 40 + 41 = 40(40 + 1) + 41 is divisible by 41, and certainly when n = 41, 41² + 41 + 41 is clearly divisible by 41.

The incredible formula  n² − 79n + 1601 was discovered, which produces 80 primes for the consecutive values n = 0 to 79. The product of the coefficients, −79 and 1601, is −126479.

Considering quadratics of the form:

n² + an + b, where |a| < 1000 and |b| < 1000
where |n| is the modulus/absolute value of n
e.g. |11| = 11 and |−4| = 4

Find the product of the coefficients, a and b, for the quadratic expression that produces the maximum number of primes for consecutive values of n, starting with n = 0.

This was another puzzle I’d ignored previously, then looked at it again and realised it wasn’t that hard after all.  First time I’ve really done anything with quadratics since school probably!

<?php
include 'isPrime.php';

$maxPrimes = 0;
$maxCoefficients = [];

calculatePrimes(1, 1);
calculatePrimes(1, -1);
calculatePrimes(-1, 1);
calculatePrimes(-1, -1);

function calculatePrimes($incrementA, $incrementB) {
	global $maxPrimes;
	$a = 0; 
	while (abs($a) < 1000) {
		$b = 0;
		while (abs($b) < 1000) {
			$n = 0;

			while (true) {
				$q = ($n * $n) + ($n * $a) + $b;
				
				if (!isPrime($q)) {
					if ($n > $maxPrimes) {
						$maxPrimes = $n;
						setMaxCoefficients($a, $b);
					}
					break;
				}
				
				$n++;
			}
			$b+= $incrementB;
		}
		$a+= $incrementA;
	}
}

function setMaxCoefficients($a, $b) {
	global $maxCoefficients;
	$maxCoefficients = [$a, $b];
}

echo $maxCoefficients[0] * $maxCoefficients[1];

So I’ve wrapped up most of the code into one function, which I call four times.  Each time I’m either adding or subtracting 1 from each of the two coefficients.  Then there’s nested loops so we’re going from zero to +/- 999 for both coefficients.  Within that we loop again, incrementing $n until our quadratic equation doesn’t return a prime number.  At that point, we check if the value of $n is more than the previous maximum number of primes.  And if it is, I update a global array with those coefficients.

After we finish looping, I then calculate the production from those stored coefficients.  Simple, but not particularly fast.

Next Page »

Blog at WordPress.com.