lo-ga-rie words, lines .. life

8Dec/090

Google maps v3

Vandaag een technisch verhaal aan de hand van de website onlinereisbeurs.nl gefocused op de scripting die nodig was voor de kaart (google maps) in combinatie met panoramio voor de foto's van de gekozen locatie.

Laten we beginnen met het makkelijkste: de HTML


* <_h2> uiteraard zonder de _ maar anders komt cuffon er weer overheen :p
Eigenlijk maken we hier alleen een div aan voor de map met een blok er overheen voor de info en een apart spannetje voor de pijl die beweegd; met "optimalisatie" voor IE6 omdat die browser zo goed omgaat met png's ...
Met de CSS er bij krijgen een we een set elementen die over elkaar heen vallen en een vlak voor Google maps:

#googleMap {
height:400px;
margin:0 6px 20px 20px;
}
#popup {
background:transparent url(../images/uitleg_bg.png) no-repeat scroll 0 0;
height:100px;
left:350px;
padding:20px 20px 0;
position:absolute;
top:281px;
width:380px;
z-index:100;
}
#popup h2 {
color:#00468E;
font-size:18px;
font-weight:bold;
}
#arrow {
color:#584436;
display:block;
font-size:12px;
font-weight:bold;
height:35px;
padding:10px 10px 0 0;
}
#arrow .blue {
color:#00468E;
}
#arrowImg {
background:transparent url(../images/pijl_icon.png) no-repeat scroll right top;
height:36px;
position:absolute;
right:13px;
top:38px;
width:32px;
}

Door goed gebruikte ID's te gebruiken zijn bijna alle elementen direct te benaderen wat een positieve invloed heeft op het laden van de pagina. In eerste instantie had ik de pijl als achtergrond van de ID arrow meegegeven met een background positie op rechts maar daar kan IE6 niets mee. Aangezien de achtergrond semi transparant is moet dit pijltje wel een png zijn; anders hebben we lelijke randen en leg dat je designer maar weer uit ;) Door het pijltje in een aparte span op te nemen verhelpen we dat probleem; een bijkomend voordeel is dat het weer iets makkelijker is om dit pijltje te animeren; al kan dat voor moderne browsers net zogoed via de de background-position properties.

Nu hebben we een opgemaakt vlak met een 2e vlak met textuele uitleg er over heen, laten we google maps er maar eens bijpakken.

var latLng = new google.maps.LatLng(-10, 150); // we zetten een bepaalde marker neer; hier later meer over
	map = new google.maps.Map(document.getElementById('googleMap'), {
		zoom: 1, // we kunnen zoomen van lvl 0 (alles tot 21; straatniveau)
		center: new google.maps.LatLng(0, 0), // wat is het centrum van de kaart?
		mapTypeId: google.maps.MapTypeId.TERRAIN, // welk type map willen we gebruiken
		mapTypeControl: false // willen we de gebruiker de controle geven over welk type map ze kunnen kiezen? Nu niet iig
	});
	var markerImg = "/include/images/koffer.png"; // plaatje gebruikt voor de marker
	marker = new google.maps.Marker({
		position: latLng, // welke positie heeft de marker
		title: 'Sleep de koffer naar uw droombestemming', // wat is de titel van de marker als je er met je muis overheen gaat?
		map: map, // welke map gebruikt deze marker; ja we kunnen meerdere maps op 1 pagina zetten
		draggable: true, // kan je deze marker slepen?
		icon: markerImg // verrek ons eerder gedefinieerde plaatje voor de marker
	});

	// Update current position info.
	geocodePosition(latLng, false); // tja leuk die lat.lon. getallen maar waar zijn we dan?

	google.maps.event.addListener(marker, 'dragend', function() {
		geocodePosition(marker.get_position(), true); // we hebben de marker versleept, update de positie
	});
function geocodePosition(pos, remove) {
	if (remove) $("#popup").hide(); // moeten we onze div met begin informatie verwijderen?
	if (remove) $.scrollTo("#googleMap", 1000); // om de focus echt op de map te leggen scrollen we daar heen zodat de map goed in beeld is
	geocoder.geocode({
		latLng: pos
	}, function(responses) {
	if (responses && responses.length > 0) {
/*
 de geocoder zal een set van resultaten terug geven, van zo precies mogelijk tot aan eigenlijk "ja je zit nog op de aarde"
Zodra we dus een set resultaten hebben gaan we bekijken welke we nu exact willen gebruiken
*/
			var lat = responses[0].geometry.location.lat(); // we nemen de meest nauwkeurige; de gegevens die wij nodig hebben zitten ook opgeslagen in dit object
			var lng = responses[0].geometry.location.lng();
/*
  Door het zoekgebied te vergroten krijg je niet alleen resultaten van de straat terug maar van de stad; onderstaande berekening is nogal grof maar werkt behoorlijk goed
*/
			var minLng = Math.floor((lat) * 100) / 100;
			var maxLng = Math.ceil((lat) * 100) / 100;

			var minLat = Math.floor((lng) * 100) / 100;
			var maxLat = Math.ceil((lng) * 100) / 100;

			// console.log("getting photo's for LAT: " + lat + ", LON: " + lng);
			// panoramio
/*
Op panoramio staan mooie foto's; meer op de omgeving dan op mensen gefocussed in tegenstelling tot flickr. Panoramio heeft ook een API om via JSON mooi een set resultaten terug te krijgen; ons zoekgebied is waar de marker gesleept is + de omgeving
*/
			$.getJSON("http://www.panoramio.com/map/get_panoramas.php?order=popularity&set=full&" +
					"from=0&to=10&minx=" + minLat + "&miny=" + minLng + "&maxx=" + maxLat + "&maxy=" + maxLng + "&size=small&callback=?",
					{},
					function(data) {
						$("#photos").html(""); // reset de div waar de foto's staan/stonden
						$(data).each(function(i, item) {
							for (var j = 0; j < item.count; j++) {
								try {
									var img = new Image();
									img.src = item.photos[j].photo_file_url; // maak een image object aan voor elke foto die we terugkrijgen, via het javascript image object zorgen we er direct voor dat deze preloaded worden
									var currentHTML = $("#photos").html();
									$("#photos").html(currentHTML + ""); // voeg de nieuwe foto toe aan de HTML
								} catch (e) { }
							} if (item.count == 0) {
								$("#photos").html("");
								// Tja geen foto's zouden we nu ons zoekscherm moeten uitbreiden misschien? versie 2.0 ;)
							}
						});
					}
				);
			if (remove) updateMarkerAddress(formatAddress(responses[0]), true); // nieuw adres = laat dit nieuwe adres ook zien
		} else {
			updateMarkerAddress('Geen locatie gevonden', false); // aah geen adres gevonden; komt eigenlijk alleen maar voor als je in de zee een straat probeert te vinden ;)
		}
	});
}
function formatAddress(address) {
	var wantedTypes = ["locality", "country"]; // welke types van het adres willen we zien? Je kan postcodes terug krijgen, huisadressen, landmarks e.d. maar wij willen nu alleen de locality (stad) en country (land)
	var formattedAddress = [];
	for (var i = 0; i < address.address_components.length; i++) { 		var component = address.address_components[i]; // een google address bestaat uit verschillende componenten, daar gaan we es doorheen 		var type = component.types[0]; 		if ($.inArray(type, wantedTypes) > -1) {
			formattedAddress.push(component.long_name); // ja dir compontent willen we!
		}
	}
	if (formattedAddress.length > 1) { // kunnen we een adres opbouwen dat uit meerdere compontenten bestaat?
		return formattedAddress[0] + " - " + formattedAddress[1]; // zo ja dan graag stad - land (volgorde bepaal je in de array
	} else {
		return formattedAddress[0]; // zo niet dan laten we maar zien wat we hebben (alleen land)
	}
}
function updateMarkerAddress(str, isAbleToGo) {
	$("#results .text").html(str); // laat maar zien
	var fontSize = 17;
	if (str.length > 13) fontSize = 15;
	if (str.length > 23) fontSize = 13;
	if (str.length > 28) fontSize = 11;
/*
 we passen de fontsize aan aan de hoeveelheid karakters
*/
	$("#results .text").css("fontSize", fontSize);
	$("#results a").attr("href", "step2.html?loc=" + escape(str)); // we sturen het adres escapped en al door naar de volgende pagina; mits je klikt natuurlijk
	(isAbleToGo) ? $("#results a").show() : $("#results a").hide(); // de knop laten we alleen zien als je er ook daadwerkelijk heen kan, helaas geen droomreis mogelijk naar de bodem van de oceaan
	$("#results").show(); // laat de div met resultaten zien.
}

Zo een lap code is het wel, maar het resultaat is er ook naar, een volledig interactieve map met een versleepbare marker die zodra je die ergens neerzet foto's uit de buurt ophaalt, het adres ophaalt, het adres formatteerd naar een gewenst formaat en vervolgens deze gegevens keurig toont.

Google maps in onlinereisbeurs.nl

Google maps in onlinereisbeurs.nl

Filed under: jquery, json, werk No Comments
15Jun/090

Native JSON

Native JSON zal in de meeste moderne browsers aanwezig zijn/komen maar natuurlijk moet je als webdeveloper altijd rekening houden met mensen die in het stenen tijdperk leven.
Je kan onderstaande code gebruiken om te kijken of native JSON aanwezig is en zo niet dan gebruik maken van een JSON parser (handig om te weten de JSON parser heeft exact de zelfde syntax, dus je checkt alleen of je een scriptje extra moet inladen of niet.

if (!window.JSON) {
  // no native json; inject the json parser code into the head of the page.
  $("head").append("");
}
var jsonObject = JSON.parse(jsonString);

de json parser kan je hier vinden. Kind kan de was doen

Filed under: json No Comments
20Apr/090

Van json naar atompub

Men neme een blog bericht met een auteur, een header en een berichtblok. Deze halen we van de server via JSON en krijgen het volgende terug: We nemen even aan dat "ci1" een ContentItem is en dat deze ID ook wordt gebruikt als ID voor de div waarin het item gerenderd wordt. Deze div heeft ook de class "editMode" (als enige op de pagina)

var ci1 = {
 "entry" : { "@xmlns" : "http://www.w3.org/2005/Atom" ,
  "title" : "[Titel] Lorem Ipsum",
  "id" : "generated-id-1",
  "updated" : "2005-10-07T17:43:07Z",
  "author" : {
   "name" : "Logged in user"
  },
  "content" : { "@type" : "xhtml",
   "div" : Lorem ipsum dolor sit amet"
  }
 }
}

Vervolgens kunnen we de content hiervan heel makkelijk aanpassen door het volgende uit te voeren:

var id = $(".editMode").attr("id");
eval(id).entry.content.div = "Andere content";

Nu willen we deze data opslaan met atompub, wat niet meer is dan een XML met bepaalde elementen, verrek net dé elementen die ik ook in de json heb gebruikt, we kunnen dus de json 1 op 1 omzetten naar XML en dat opsturen naar de server

$.post("/atompub.ashx", { data: json2xml(eval(id))},
 function(data){
   // ga maar wat leukt doen met de return value
 }, "xml");

De gebruikte json2xml komt van deze site
Al met al een geslaagd dagje. Even wat technieken aan elkaar knopen en je hebt een volledig in javascript geschreven client/server oplossing die robuust maar toch veelzijdig is. Door gebruik te maken van json is de data erg makkelijk aan te passen en atompub zorgt voor de omsluiting (het lijkt wel soap ...)

Filed under: atompub, jquery, json, werk No Comments