Skip to main content

Source code file content

Revision: 26

» Project Revision History

» Checkout URL

respository / geo.html

Size: 11683 bytes, 1 line
<html>
<head>
<title>Test Geo Routines</title>
<style type="text/css">
div.fl { float: left; }
#map { width: 640px; height: 480px; }
</style>
</head>
<body>
Test Geo Routines
<br/>
<div class="fl">
<script type="text/javascript">

var Akme = {
	onLoad : function (fn) {
		if ("addEventListener" in window) window.addEventListener("load", fn, false);
		else if ("attachEvent" in window) window.attachEvent("onload", fn);
	},

	onUnload : function (fn) {
		if ("addEventListener" in window) window.addEventListener("unload", fn, false);
		else if ("attachEvent" in window) window.attachEvent("onunload", fn);
	},

	onEvent : function (elem, evnt, fn) {
		if ("addEventListener" in elem) elem.addEventListener(evnt, fn, false);
		else if ("attachEvent" in elem) elem.attachEvent("on"+evnt, fn);
	},

	getBaseHref : function () {
		var a = document.getElementsByTagName("base");
		return a.length != 0 ? a[0]["href"] : "";
	}
}

/**
 * Geographic lat,lon, meters-on-earth utilities.
 *
 * @see http://www.movable-type.co.uk/scripts/latlong.html
 * @see http://en.wikipedia.org/wiki/Latitude
 * @see http://en.wikipedia.org/wiki/Longitude
 * @see http://www.faqs.org/faqs/geography/infosystems-faq/ see 5.1a
 */
var AkmeGeo = {
	// Zero degrees angle is due North.
	// Rounding latitude/longitude to 6 decimals is near 1cm accuracy on Earth.
	radiusM : (6378137+6356752)/2,
	radiusEquatorialM : 6378137,
	radiusPolarM : 6356752,
	toRad : function (deg) { return deg * Math.PI / 180; },
	toDeg : function (rad) { return rad * 180 / Math.PI; },
	round6 : function (f) { return Math.floor(f*100000.0+0.5)/100000.0; },
	metersPerDegreeLat : function (lat) {
		lat = this.toRad(lat);
		return this.round6( 111132.954 - 559.822*(Math.cos(2.0*lat)) + 1.175*(Math.cos(4.0*lat)) );
	/*
		lat = this.toRad(lat);
		var c = this.radiusEquatorialM * this.radiusPolarM;
		var a = this.radiusEquatorialM * Math.cos(lat);
		var b = this.radiusPolarM * Math.sin(lat);
		return this.round6(
			c*c / Math.pow(a*a + b*b, 1.5) * Math.PI/180
			);
	*/
	},
	metersPerDegreeLon : function (lat /* still given latitude */) {
		lat = this.toRad(lat);
		return this.round6( this.radiusEquatorialM * Math.cos(Math.atan(0.99664719*Math.tan(lat))) * Math.PI/180 );
	/*
		lat = this.toRad(lat);
		var c = Math.cos(lat);
		var a = this.radiusEquatorialM * c;
		var b = this.radiusPolarM * Math.sin(lat);
		return this.round6(
			c * this.radiusEquatorialM*this.radiusEquatorialM / Math.sqrt(a*a + b*b) * Math.PI/180
			);
	*/
	},
	moveAngleDistance : function (lat0lon1, angle /* North is zero */, distanceM /* meters */) {
		// d = angular distance covered on earth's surface
		var d = parseFloat(distanceM)/this.radiusM;
		var lat1 = this.toRad(lat0lon1[0]), lon1 = this.toRad(lat0lon1[1]);
		angle = this.toRad(angle);
		var lat2 = lat1 + d*Math.cos(angle);
		var dLat = lat2-lat1;
		var dPhi = Math.log(Math.tan(lat2/2+Math.PI/4)/Math.tan(lat1/2+Math.PI/4));
		var q = (!isNaN(dLat/dPhi)) ? dLat/dPhi : Math.cos(lat1);  // E-W line gives dPhi=0
		var dLon = d*Math.sin(angle)/q;
		// check for some daft bugger going past the pole
		if (Math.abs(lat2) > Math.PI/2) lat2 = lat2>0 ? Math.PI-lat2 : -(Math.PI-lat2);
		lon2 = (lon1+dLon+3*Math.PI)%(2*Math.PI) - Math.PI;
		lat0lon1[0] = this.round6(this.toDeg(lat2));
		lat0lon1[1] = this.round6(this.toDeg(lon2));
		return lat0lon1;
	},
	/**
	 * To match other lat/lon within a distance, either use a square [] at 0.9,
	 * a 2-rectangle cross +, or 3-rectangle combination to approximate a circle.
	 *   _|_
	 *  | | |
	 * -------
	 *  |_|_|
	 *    |
	 * PI(1)^2 / (1*2)^2 = 0.7854 (circle/square) so use 0.89 (sqrt(PI*(1)^2)=0.8862) of the radius desired.
	 */
	calculateEarthDistanceM : function (lat1, lon1, lat2, lon2) {
		/*
		Polar Radius:      6,356.6 km
		Equatorial Radius: 6,378.2 km 
		The Haversine formula according to Dr. Math.
		http://mathforum.org/library/drmath/view/51879.html
					
				lon = lon2 - lon1
				lat = lat2 - lat1
				a = (sin(lat/2))^2 + cos(lat1) * cos(lat2) * (sin(lon/2))^2
				c = 2 * atan2(sqrt(a), sqrt(1-a)) 
				d = c * R
					
				Where
					* lon is the change in longitude
					* lat is the change in latitude
					* c is the great circle distance in Radians.
					* R is the radius of a spherical Earth.
					* The locations of the two points in 
						spherical coordinates (longitude and 
						latitude) are lon1,lat1 and lon2, lat2.
		*/
		var lat1rad = lat1 * (Math.PI / 180.0);
		var lon1rad = lon1 * (Math.PI / 180.0);
		var lat2rad = lat2 * (Math.PI / 180.0);
		var lon2rad = lon2 * (Math.PI / 180.0);

		var lat = lat2rad - lat1rad;
		var lon = lon2rad - lon1rad;

		// Intermediate result a.
		var a = Math.pow(Math.sin(lat / 2.0), 2.0) + 
		   Math.cos(lat1rad) * Math.cos(lat2rad) * 
		   Math.pow(Math.sin(lon / 2.0), 2.0);

		// Intermediate result c (great circle distance in Radians).
		var c = 2.0 * Math.atan2(Math.sqrt(a), Math.sqrt(1.0 - a));

		// Distance.
		return c * this.radiusM;
	}

};

document.writeln("<br/>");
document.writeln("<br/>Latitude Meters:");
document.writeln("<br/>+90:"+AkmeGeo.metersPerDegreeLat(90.0));
document.writeln("<br/>+45:"+AkmeGeo.metersPerDegreeLat(45.0));
document.writeln("<br/>+00:"+AkmeGeo.metersPerDegreeLat(0.0));
document.writeln("<br/>-45:"+AkmeGeo.metersPerDegreeLat(-45.0));
document.writeln("<br/>-90:"+AkmeGeo.metersPerDegreeLat(-90.0));
document.writeln("<br/>");
document.writeln("<br/>Longitude Meters:");
document.writeln("<br/>+90:"+AkmeGeo.metersPerDegreeLon(90.0));
document.writeln("<br/>+45:"+AkmeGeo.metersPerDegreeLon(45.0));
document.writeln("<br/>+00:"+AkmeGeo.metersPerDegreeLon(0.0));
document.writeln("<br/>-45:"+AkmeGeo.metersPerDegreeLon(-45.0));
document.writeln("<br/>-90:"+AkmeGeo.metersPerDegreeLon(-90.0));

var placesLatLon = {
	"Toronto": [43.652472,-79.381714]
};

var lat0lon1 = placesLatLon["Toronto"].slice(0);
document.writeln("<br/>");
document.writeln("<br/>Toronto:"+ AkmeGeo.round6(lat0lon1[0]) +","+ AkmeGeo.round6(lat0lon1[1]));
document.writeln("<br/>Toronto-NE100km:"+AkmeGeo.moveAngleDistance(lat0lon1, 45, 100*1000));


</script>
<script type="text/javascript" src="http://www.google.com/jsapi?key=ABQIAAAAJIKekVojTNmzgujks7uHeRSKrfyCBnRhz1cxZVKgi-LNocTDsxQGGcV6xOHNw1_HADDCgC5Vg6Sa5Q"></script>
<script type="text/javascript" >

var gsearch;
var goptions;
var gsearchControl;
var searchResults = [];

// When using .setCenterPoint(map), the sspn value can be computed using: myMap.getBounds().toSpan().toUrlValue();
// (e.g., sspn=0.065169,0.194149).
google.load("maps", "2", {"other_params":"sensor=false&key=ABQIAAAAJIKekVojTNmzgujks7uHeRSKrfyCBnRhz1cxZVKgi-LNocTDsxQGGcV6xOHNw1_HADDCgC5Vg6Sa5Q"});
google.load("search", "1", {"other_params":"sensor=false&key=ABQIAAAAJIKekVojTNmzgujks7uHeRSKrfyCBnRhz1cxZVKgi-LNocTDsxQGGcV6xOHNw1_HADDCgC5Vg6Sa5Q"});

var map;
google.setOnLoadCallback(function () {
	map = new google.maps.Map2(document.getElementById("map"));
	map.addControl(new google.maps.SmallMapControl());
	map.addControl(new google.maps.MapTypeControl());

	var place = placesLatLon["Toronto"];
	map.setCenter(new GLatLng(place[0], place[1]), 11);
	map.getCenterOld = map.getCenter;
	map.getCenter = function() {
		var box = boxer.shapes[0];
		if (!box) return this.getCenterOld();
		var bounds = box.getBounds();
		var sw = bounds.getSouthWest(), ne = bounds.getNorthEast();
		return new GLatLng((ne.lat() + sw.lat()) / 2, (ne.lng() + sw.lng()) / 2); 
	};
	map.getBoundsOld = map.getBounds;
	map.getBounds = function() {
		var box = boxer.shapes[0];
		return (box) ? box.getBounds() : this.getBoundsOld();
	};
	gsearch = new google.search.LocalSearch();
	gsearch.setResultSetSize(8);
	gsearch.setCenterPoint(map);
	//goptions = new google.search.SearcherOptions();
	//goptions.setExpandMode(google.search.SearchControl.EXPAND_MODE_OPEN);
	//gsearchControl = new google.search.SearchControl();
	//gsearchControl.addSearcher(gsearch, goptions);
	gsearch.setSearchCompleteCallback(gsearch, function() {
		if (!gsearch.cursor) return;
	    if (gsearch.cursor.currentPageIndex == 0) {
			//map.clearOverlays();
			for (var i=0; i<searchResults.length; i++) map.removeOverlay(searchResults[i]);
			searchResults.length = 0;
		}
		var results = gsearch.results; // Grab the results array
		// We loop through to get the points
		for (var i = 0; i < results.length; i++) {
		  var result = results[i]; // Get the specific result
		  var markerLatLng = new google.maps.LatLng(parseFloat(result.lat),parseFloat(result.lng));
		  if (boxer.shapes[0] && !boxer.shapes[0].getBounds().containsLatLng(markerLatLng)) {
			continue;
		  }
		  var marker = new google.maps.Marker(markerLatLng); // Create the marker
		  // Bind information for the infoWindow aka the map marker popup
		  marker.bindInfoWindow(result.html.cloneNode(true));
		  result.marker = marker; // bind the marker to the result
		  map.addOverlay(marker); // add the marker to the map
		  searchResults[searchResults.length] = marker;
		}
		if (gsearch.cursor.currentPageIndex+1 < gsearch.cursor.pages.length) {
			gsearch.gotoPage(gsearch.cursor.currentPageIndex+1);
		} else {
			//alert(searchResults.length);
		}
	});
	//gsearchControl.draw(document.getElementById("searchControl"));
	document.getElementById("searchControl").style.display = "";
});

var boxer = {
	lock : [true],
	color : "#888800",
	options : {maxVertices: 5}, // 5 to land on starting point again.
	shapes : [],
	listeners : [],

	getColor : function () { return this.color; },
	
	startShape : function () {
	  // guard against multi-click
	  if (!this.lock.pop()) return;
	  var polygon;
	  while ((polygon = this.shapes.pop())) map.removeOverlay(polygon);
	  var color = this.getColor();
	  var polygon = new GPolygon([], color, 2, 0.7, color, 0.2);
	  this.shapes.push(polygon);
	  this.startDrawing(polygon, color);
	},
	
	startDrawing : function (poly, color) {
	  map.addOverlay(poly);
	  poly.enableDrawing(this.options);
	  //poly.enableEditing({onEvent: "mouseover"});
	  poly.disableEditing({onEvent: "mouseout"});
	  var myColor = color;
	  var lock = this.lock;
	  // lineupdated, endline, cancelline, 
	  var listeners = this.listeners;
	  listeners.push(GEvent.addListener(poly, "lineupdated", function() {
		  if (poly.getVertexCount() < 2) return;
		  var old = listeners.pop();
		  if (old) GEvent.removeListener(old);
		  poly.disableEditing();
		  // Create a closed square based on the diagonal.
		  var bounds = poly.getBounds();
		  var sw = bounds.getSouthWest(), ne = bounds.getNorthEast();
		  for (var i=poly.getVertexCount()-1; i>=0; i--) poly.deleteVertex(i);
		  var i=0;
		  poly.insertVertex(i++, sw);
		  poly.insertVertex(i++, new GLatLng(ne.lat(), sw.lng()));
		  poly.insertVertex(i++, ne);
		  poly.insertVertex(i++, new GLatLng(sw.lat(), ne.lng()));
		  // Google says to close the polygon back to its initial point.
		  poly.insertVertex(i++, sw);
		  lock.push(true);
	  }));
	}
};

</script>
</div>
<div id="searchControl" class="fl">
<div id="map" ></div>
<form name="competitorSearchForm" onsubmit='try { gsearch.execute(this.searchText.value); } finally { return false; }'>
<input type="text" name="searchText" value="McDonalds"/>
<input type="submit" name="submitBtn" value="Show Competitors (Up to 32)"/>
<input type="button" name="boxBtn" value="Draw Box" onclick="boxer.startShape()"/>
</form>
</div>

</body>
</html>
 
 
Close
loading
Please Confirm
Close