Tag Archives: idw

OpenWeatherMap IDW

idwOpenWeatherMap has a nice API that allows you to consume live weather data. I have made other simple examples here (v2.1) and here (v2.0 – doesn’t work), but you should check out the OWM API documentation and examples for more.

This example (v2.5) collects weather info from several stations across British Columbia and Alberta, interpolates temperature values between the stations using the Inverse Distance Weighting method, and displays both the stations (points) and interpolated surface (raster) as PaperJS objects.

 <!DOCTYPE html>
 <html>
 <head>
 <!---------------------
 Code by Darren Wiens
 dkwiens@gmail.com
 ----------------------->
 <style type="text/css">
 html { height: 100% }
 #map { height: 500px; width: 500px; }
 canvas { position: absolute; top: 10; left: 10; z-index: 1; pointer-events:none; }
 </style>
 <!-- Load the Paper.js library -->
 <script type="text/javascript" src="http://darrenwiens.net/scripts/paper.js"></script>
 <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
 <script src="http://code.jquery.com/jquery-1.8.2.js"></script>
 <!-- Define inlined PaperScript associate it with myCanvas -->
 <script type="text/paperscript" canvas="myCanvas">
 var maxWidth = 500;
 var maxHeight = 500;
 var maxPoint = new Point(maxWidth,maxHeight);
var rect = new Path.Rectangle(0,0,maxWidth, maxHeight);
 rect.strokeColor = 'black';
 rect.strokeWidth = 3;
var map;
 var sampleCirs = new Group();
 var texts = new Group();
 var interpRaster = new Raster('myImg');
 var minTemp = 1000;
 var maxTemp = -1000;
function onFrame(event)
 {
}
function pointToLatLng(point)
 {
 var proj = map.getProjection();
 var bounds = map.getBounds();
 var ne = bounds.getNorthEast();
 var sw = bounds.getSouthWest();
 var neWorldXY = proj.fromLatLngToPoint(ne);
 var swWorldXY = proj.fromLatLngToPoint(sw);
 var curPixelX = point.x / Math.pow(2,map.getZoom());
 var curPixelY = point.y / Math.pow(2,map.getZoom());
 var curWorldX = curPixelX + swWorldXY.x;
 var curWorldY = curPixelY + neWorldXY.y;
 var curWorldPoint = new google.maps.Point(curWorldX,curWorldY);
 var curLatLng = proj.fromPointToLatLng(curWorldPoint);
 return curLatLng;
 }
function latLngToPoint(latLng)
 {
 var proj = map.getProjection();
 var calWorldPoint = proj.fromLatLngToPoint(latLng);
 var calPixelPointx = calWorldPoint.x * Math.pow(2,map.getZoom());
 var calPixelPointy = calWorldPoint.y * Math.pow(2,map.getZoom());
 var bounds = map.getBounds();
 var ne = bounds.getNorthEast();
 var sw = bounds.getSouthWest();
 var neWorldPoint = proj.fromLatLngToPoint(ne);
 var swWorldPoint = proj.fromLatLngToPoint(sw);
 var ePixelPoint = neWorldPoint.x * Math.pow(2,map.getZoom());
 var nPixelPoint = neWorldPoint.y * Math.pow(2,map.getZoom());
 var wPixelPoint = swWorldPoint.x * Math.pow(2,map.getZoom());
 var sPixelPoint = swWorldPoint.y * Math.pow(2,map.getZoom());
 var screenPixelX = calPixelPointx - wPixelPoint;
 var screenPixelY = calPixelPointy - nPixelPoint;
 var point = new Point(screenPixelX, screenPixelY);
 return point;
 }
function drawPoints(s)
 {
 childCount = 0;
 for (var station=0;station<s.length;station++) {
 if (s[station].last.hasOwnProperty('main')) {
 if (s[station].last.main.hasOwnProperty('temp')) {
 var lat = s[station].station.coord.lat;
 var lng = s[station].station.coord.lon;
 var newPoint = new Point(latLngToPoint(new google.maps.LatLng(lat, lng)));
 sampleCirs.addChild(new Path.Circle(newPoint,2));
 sampleCirs.children[childCount].value = s[station].last.main.temp - 273.15;
 sampleCirs.children[childCount].latlng = pointToLatLng(sampleCirs.children[childCount].position);
 if (sampleCirs.children[childCount].value < minTemp) {
 minTemp = sampleCirs.children[childCount].value;
 }
 if (sampleCirs.children[childCount].value > maxTemp) {
 maxTemp = sampleCirs.children[childCount].value;
 }
 sampleCirs.strokeColor = 'black';
 sampleCirs.fillColor = 'black';
 var text = new PointText(newPoint + new Point(3,-3));
 text.content = Math.round(sampleCirs.children[childCount].value);
 text.latlng = pointToLatLng(text.position);
 texts.addChild(text);
 childCount++;
 }
 }
 }
 drawRaster();
 }
function drawRaster()
 {
 interpRaster.size = new Size(100,100);
 interpRaster.fitBounds(view.bounds);
 for (var i=0;i<100;i++)
 {
 for (var j=0;j<100;j++)
 {
 var wj = 0;
 var wis = [];
 for (var k=0;k<sampleCirs.children.length;k++)
 {
 var dx = sampleCirs.children[k].position.x - (i * 5);
 var dy = sampleCirs.children[k].position.y - (j * 5);
 var dk = Math.sqrt(Math.pow(dx,2) + Math.pow(dy,2));
 var p = 3;
 var wj_inst = 1/(Math.pow(dk,p))
 wj += wj_inst;
 wis.push(wj_inst*sampleCirs.children[k].value);
 }
 var u = 0;
 for (var l=0;l<wis.length;l++)
 {
 u += wis[l]/wj;
 }
 var scale = (u-minTemp)/(maxTemp-minTemp);
 interpRaster.setPixel(i,j,new RgbColor(scale,0,1-scale,1));
 }
 }
 interpRaster.opacity = 0.7;
 interpRaster.latlng = map.getCenter();
 interpRaster.TLlatlng = pointToLatLng(interpRaster.bounds.topLeft);
 interpRaster.BRlatlng = pointToLatLng(interpRaster.bounds.bottomRight);
 interpRaster.moveBelow(sampleCirs);
 console.log("MAX: " + maxTemp);
 console.log("MIN: " + minTemp);
 }
function getData(s){
 drawPoints(s);
 }
$(document).ready(function() {
 var style = [
 { "elementType": "geometry.fill",
 "stylers": [ { "visibility": "off" } ]
 },{ "featureType":
 "road",
 "elementType": "geometry.fill",
 "stylers": [ { "visibility": "on" },
 { "color": "#000000" } ]
 },{ "featureType": "water",
 "elementType": "geometry.fill",
 "stylers": [ { "color": "#000000" } ]
 } ]
var myLatlng = new google.maps.LatLng(53.917, -122.75);
 var myOptions = {
 zoom: 5,
 center: myLatlng,
 mapTypeId: google.maps.MapTypeId.ROADMAP,
 disableDefaultUI: true
 }
 map = new google.maps.Map(document.getElementById("map"), myOptions);
 map.setOptions({styles: style});
google.maps.event.addListener(map, 'projection_changed', function() {
 $.getJSON('http://api.openweathermap.org/data/2.5/station/find?lat=53.917&lon=-122.75&cnt=30', getData); // API v.2.5
 });
google.maps.event.addListener(map, 'center_changed', function() {
 for (var i=0;i<sampleCirs.children.length;i++)
 {
 sampleCirs.children[i].position = latLngToPoint(sampleCirs.children[i].latlng);
 sampleCirs.children[i].latlng = pointToLatLng(sampleCirs.children[i].position);
 texts.children[i].position = latLngToPoint(texts.children[i].latlng);
 texts.children[i].latlng = pointToLatLng(texts.children[i].position);
 }
 interpRaster.position = latLngToPoint(interpRaster.latlng);
 interpRaster.latlng = pointToLatLng(interpRaster.position);
 });
google.maps.event.addListener(map, 'zoom_changed', function() {
 for (var i=0;i<sampleCirs.children.length;i++)
 {
 sampleCirs.children[i].position = latLngToPoint(sampleCirs.children[i].latlng);
 sampleCirs.children[i].latlng = pointToLatLng(sampleCirs.children[i].position);
 texts.children[i].position = sampleCirs.children[i].position + new Point(3,-3);
 texts.children[i].latlng = pointToLatLng(texts.children[i].position);
 }
 var tl = latLngToPoint(interpRaster.TLlatlng);
 var br = latLngToPoint(interpRaster.BRlatlng);
 var bounds = new Rectangle(tl,br);
interpRaster.fitBounds(bounds);
 interpRaster.TLlatlng = pointToLatLng(interpRaster.bounds.topLeft);
 interpRaster.BRlatlng = pointToLatLng(interpRaster.bounds.bottomRight);
 });
 });
</script>
 </head>
 <body>
 <canvas id="myCanvas" width="500" height="500"></canvas>
 <div id="map" width="500" height="500"></div>
 <img id="myImg"></img>
 </body>
 </html>
Advertisements

Magical Mystery Contours (raster to contour algorithm)

Image

I added contour lines (map here) to my IDW map (read here). The map has contour breaks at 25, 50, and 75. I was really flying by the seat of my pants coming up with this algorithm – it’s pretty computationally intensive, but it does run (at least on Firefox), so don’t panic if the “This script is non-responsive” pop-up pops up. If there’s any demand for it, I’ll write up an explanation of what’s going on, but until then you’ll have to figure it out from the source code (good luck). And, if you’ve got a more streamlined raster to contour routine, I’d love to hear about it!

IDW on Google Map

Image

Inspired by bad weather this afternoon, I made this basic Inverse Distance Weighted interpolator, written in JavaScript, displayed by PaperJS objects on top of a Google Map. It makes several randomly placed points with random values, then interpolates the colored surface based on the distance-weighted influence of the points. There are lots of geographic applications that may use this technique (rain, temperature surfaces, etc.), but I’m not feeling too inspired to go further right now. Let me know if you do!