Last active
August 29, 2015 14:03
-
-
Save shshaw/9e49ea23d68eb761701b to your computer and use it in GitHub Desktop.
SDI Map
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// jQuery's removeClass doesn't work for SVG, but this does! | |
// takes the object obj to remove from, and removes class remove | |
// returns true if successful, false if remove does not exist in obj | |
(function($){ | |
$.fn.addClassSVG = function(addclass) { | |
var elem, | |
i = 0, | |
len = this.length; | |
for (; i < len; i++ ) { | |
elem = this[ i ]; | |
var classes = $(elem).attr('class'); | |
var index = classes.search(addclass); | |
if (index !== -1) { return this; } | |
else { | |
classes = classes + " " + addclass; | |
$(elem).attr('class', classes); | |
return this; | |
} | |
} | |
return this; | |
}; | |
$.fn.removeClassSVG = function(remove) { | |
var elem, | |
i = 0, | |
len = this.length; | |
for (; i < len; i++ ) { | |
elem = this[ i ]; | |
var classes = $(elem).attr('class'); | |
var index = classes.search(remove); | |
if (index === -1) { return this; } | |
else { | |
classes = classes.substring(0, index) + classes.substring((index + remove.length), classes.length).trim(); | |
$(elem).attr('class', classes); | |
return this; | |
} | |
} | |
return this; | |
}; | |
})(jQuery); | |
var LocationMap = function(target) { | |
showLocations = function(country) { | |
if ( currentCountry !== country ) { | |
currentCountry = country; | |
showMarkers(country); | |
showListing(country); | |
centerMap(country); | |
} else { | |
currentCountry = ""; | |
showMarkers(); | |
resetMap(); | |
hideListing(country); | |
} | |
resetPoints(); | |
hideTooltip(); | |
}; | |
hideLocations = function(country) { | |
currentCountry = ""; | |
showMarkers(); | |
// hideMarkers(); | |
resetMap(); | |
hideListing(country); | |
resetPoints(); | |
hideTooltip(); | |
}; | |
var $target = $(target); | |
var width, | |
height, | |
mapCenterX, | |
mapCenterY, | |
mapOffsetTop; | |
var map = d3.select(target); | |
var svgContainer = map | |
.append("svg").attr("id","Map-SVG"); | |
var svg = svgContainer | |
.append("g").attr("id","Map-Group"); | |
$("#Map-SVG, #Map-Group").attr("width", "100%").attr("height", "100%"); | |
this.resizeMap = function() { | |
width = $target.outerWidth(); | |
height = $target.outerHeight(); | |
mapCenterX = width/2; | |
mapCenterY = height/2; | |
mapTranslate = [mapCenterX, 205]; | |
mapOffsetTop = $target.offset().top; | |
svg.attr("width", width).attr("height", height); | |
// console.log("mapCenter",mapCenterX); | |
}; | |
this.resizeMap(); | |
var mapScale = 100; | |
var svgScaleMax = 4; | |
svgScaleMin = 0.75; | |
var projection = d3.geo.mercator() | |
.translate(mapTranslate) | |
.scale(mapScale); | |
var path = d3.geo.path() | |
.projection(projection); | |
var markers = [], | |
markerRadius = 2.5, | |
currentCountry; | |
var mapCountries = svg.append("g").attr("id","Map-Countries"); | |
var renderMap = function (error, world) { | |
$target.addClass("is-Loaded"); | |
var land = topojson.object(world,world.objects.land).geometries, | |
landPaths = mapCountries | |
.selectAll() | |
.data(land) | |
.enter() | |
.append("path") | |
.attr("class", "land") | |
.attr("d", path); | |
}; | |
queue() | |
.defer(d3.json, "/content/themes/sdi/js/data/world.json") | |
.await(renderMap); | |
var resetPoints = function () { | |
d3.select(".Location.is-Selected").classed("is-Selected",false).attr("r", markerRadius); | |
}; | |
var tooltip = $("<div id='Location-Details'></div>").hide().appendTo(".Locations-Map"), | |
tooltipContent = $("<div class='Content'></div>").appendTo(tooltip), | |
tooltipCurrently = $("<aside class='Currently'>Currently </aside>").appendTo(tooltip), | |
currentTime = $("<time class='Time Accent' />").appendTo(tooltipCurrently), | |
tooltipWeather = $("<span class='Weather'></span>").appendTo(tooltipCurrently), | |
tooltipVisible = false; | |
//////////////////////////////////////// | |
// Get Point Weather | |
var getPointWeather = function (point) { | |
var $point = $(point); | |
if ( $point.data("weather") !== undefined ) { | |
return $point.data("weather"); | |
} else { | |
//////////////////////////////////////// | |
// Weather AJAX | |
tooltipWeather.html('').addClass("is-Loading"); | |
var latlng = $point.data("latlng").split(","); | |
// Yahoo's Geo.PlaceFinder API http://developer.yahoo.com/geo/placefinder/ We are passing the R gflag for reverse geocoding (coordinates to place name) | |
var geoAPI = 'SELECT woeid FROM geo.placefinder WHERE text="'+latlng[0]+','+latlng[1]+'" and gflags="R"', | |
geoYQL = 'http://query.yahooapis.com/v1/public/yql?q='+encodeURIComponent(geoAPI)+'&format=json&callback=?'; | |
// console.log("geoAPI",geoAPI); | |
// Fetch the WOEID from coordinates | |
$.ajax({url: geoYQL,dataType: 'jsonp',success: function(data){ | |
if ( data.query.count >= 1 ){ | |
var woeid = data.query.results.Result.woeid; | |
// console.log("woeid",woeid); | |
// Forming the query for Yahoo's weather API with YQL http://developer.yahoo.com/weather/ | |
var wsql = 'select item.condition from weather.forecast where woeid=WID', // and u="F" | |
weatherYQL = 'http://query.yahooapis.com/v1/public/yql?q='+encodeURIComponent(wsql)+'&format=json&callback=?'; | |
// Make a weather API request: | |
$.ajax({url: weatherYQL.replace('WID',woeid),dataType: 'jsonp', | |
success: function(data){ | |
// console.log("weather data",data); | |
if (data.query.count === 1) { | |
var f = data.query.results.channel.item.condition.temp; | |
var c = Math.round(( f -32 ) * 5 / 9); | |
$point.data('weather',', <strong class="Temp Farenheit Accent">'+f+'°F</strong> <strong class="Temp Celsius Accent">('+c+'°C)</strong>'); | |
} else { | |
$point.data('weather',false); | |
return false; | |
} | |
},error: function(){ | |
$point.data('weather',false); | |
return false; | |
},complete: function(){ | |
showTooltip(point,true); | |
} | |
}); | |
} | |
},error: function(){ | |
$point.data('weather',false); | |
return false; | |
}}); | |
} | |
}; | |
//////////////////////////////////////// | |
// Get Point Address | |
var getPointDetails = function (point) { | |
var $point = $(point), | |
country = $point.data("country"), | |
output = false; | |
showListing(country); | |
if ( $point.data("details") ) { | |
output = $point.data("details"); | |
} else { | |
var address = $("#Location-"+$point.data("id")).html(); | |
output = $("<div class='Address vcard "+country+"'>"+address+"</div>"); | |
} | |
$point.data("details",output); | |
return output; | |
}; | |
var showWeather = function(point,update) { | |
var weather = getPointWeather(point); | |
if ( update === true && weather !== undefined ) { | |
tooltipWeather.stop().fadeOut(maxAnimation,function(){ | |
tooltipWeather | |
.removeClass("is-Loading") | |
.html(weather) | |
.fadeIn(maxAnimation); | |
}); | |
} else if ( weather !== undefined ) { | |
tooltipWeather.removeClass("is-Loading").html(weather); | |
} else { | |
tooltipWeather.html(""); | |
} | |
}; | |
var timeUpdating = false; | |
var timeTimer; | |
var showTime = function(timezoneid,forceUpdate) { | |
// console.log("timezone",timezoneid,timeUpdating,forceUpdate); | |
if ( forceUpdate || !tooltipVisible ) { | |
// console.log("clearing timeout"); | |
window.clearTimeout(timeTimer); | |
timeUpdating = false; | |
} | |
if ( timezoneid === undefined ) { | |
currentTime.hide().html(""); | |
} else if ( timezoneid && ( forceUpdate || !timeUpdating) ) { | |
try { | |
currentTime.show(); | |
timeUpdating = true; | |
var time = moment().tz(timezoneid); | |
currentTime.html(time.format("h") + "<span>:</span>" + time.format("mm a")); | |
// + " <small>"+ moment.utc().zone(offset).format("dddd, MMMM Do YYYY, h:mm:ss a") + "</small>"); | |
timeTimer = window.setTimeout(function(){ | |
timeUpdating = false; | |
showTime(timezoneid, false); | |
},10000); | |
} catch(err) { | |
currentTime.hide().html(""); | |
} | |
} | |
}; | |
//////////////////////////////////////// | |
// Show Location Tooltip | |
var showTooltip = function(point,update) { | |
if ( !tooltipVisible ) { | |
tooltipVisible = true; | |
redrawMap(); | |
} | |
tooltip.addClass($point.data("country")); | |
tooltipContent.html(getPointDetails(point)); | |
if ( Modernizr.mq('(min-width: 600px)') ) { | |
showWeather(point); | |
showTime($(point).data("timezoneid"),true); | |
} | |
tooltip.not(":visible").fadeIn(maxAnimation); | |
}; | |
var hideTooltip = function() { | |
tooltipVisible = false; | |
redrawMap(); | |
tooltip.fadeOut(maxAnimation,function(){ tooltip.attr("class",""); }); | |
}; | |
//////////////////////////////////////// | |
// Update Points on Map | |
this.buildMarkers = function () { | |
$.each(locations,function(index,location){ | |
var country = index; | |
var countryMarkersId = country+"Markers"; | |
var countryMarkers = $("#"+countryMarkersId); | |
if ( ! countryMarkers.length ) { | |
countryMarkersSvg = svg.append("g").attr("id",countryMarkersId).attr("class","hide Country-Markers "+country); | |
} | |
$.each(location.posts,function(index,post){ | |
var el = post; | |
if ( el.latlng !== "" && typeof el.latlng !== 'undefined' ) { | |
var latlng = el.latlng.split(","), | |
coord = projection([latlng[1],latlng[0]]); | |
point = countryMarkersSvg.append("svg:circle") | |
.attr("class","Location Marker") | |
.attr("id", "Marker-"+el.id) | |
.attr("cx", coord[0]).attr("cy", coord[1]) | |
.attr("r", markerRadius) | |
.attr("data-id", el.id) | |
.attr("data-country", country) | |
.attr("data-latlng",latlng) | |
.attr("data-timezone", post.custom_fields.timezone_offset) | |
.attr("data-timezoneid", post.custom_fields.timezone_id) | |
.on({ | |
click : function() { | |
highlightPoint(this); | |
}, | |
mouseover : function() { | |
d3.select(this).classed("is-Hover",true); | |
}, | |
mouseout : function() { | |
d3.select(this).classed("is-Hover",false); | |
} | |
}); | |
point.data = { coord: el.latlng }; | |
markers.push(point); | |
} | |
}); | |
}); | |
showMarkers(); | |
}; | |
//////////////////////////////////////// | |
// Update Points on Map | |
var showMarkers = function (country) { | |
if ( country ) { | |
var countryMarkersId = country+"Markers"; | |
var countryMarkers = $("#"+countryMarkersId); | |
d3.select(".Country-Markers.is-Selected").classed("is-Selected",false); | |
$(".Country-Markers") | |
.not(countryMarkers) | |
.fadeOut(maxAnimation); | |
// .removeClass("is-Selected"); | |
countryMarkers | |
.fadeIn(maxAnimation) | |
.addClassSVG("is-Selected"); | |
} else { | |
d3.select(".Country-Markers.is-Selected").classed("is-Selected",false); | |
$(".Country-Markers") | |
.fadeIn(maxAnimation); | |
// .removeClass("is-Selected"); | |
} | |
}; | |
var hideMarkers = function() { | |
d3.select(".Country-Markers.is-Selected").classed("is-Selected",false); | |
$(".Country-Markers") | |
.fadeOut(maxAnimation); | |
// .removeClass("is-Selected"); | |
}; | |
$locationNavItems.hover(function(){ | |
var country = $(this).data("country"); | |
if ( country ) { | |
var countryMarkersId = country+"Markers"; | |
var countryMarkers = $("#"+countryMarkersId).addClassSVG("is-Hover"); | |
} | |
},function(){ | |
var country = $(this).data("country"); | |
if ( country ) { | |
var countryMarkersId = country+"Markers"; | |
var countryMarkers = $("#"+countryMarkersId).removeClassSVG("is-Hover"); | |
} | |
}); | |
//////////////////////////////////////// | |
// Map Zooming & panning | |
//////////////////////////////////////// | |
//////////////////////////////////////// | |
// Highlight a Point on Map | |
var highlightPoint = function (point) { | |
resetPoints(); | |
d3.select(point).classed("is-Selected",true).attr("r", markerRadius*1.5); | |
$point = $(point); | |
$point.parent().append($point); | |
showTooltip(point); | |
centerMap(point); | |
}; | |
//////////////////////////////////////// | |
// Set Min/Max on Scale | |
var scaleBounds = function(scale) { | |
if ( scale > svgScaleMax ) scale = svgScaleMax; | |
if ( scale < svgScaleMin ) scale = svgScaleMin; | |
return scale; | |
}; | |
//////////////////////////////////////// | |
// Apply translate & scale to Map SVG | |
var redrawMap = function(translate,scale,transition) { | |
if (typeof scale !== 'undefined' ) { myZoom.scale(scaleBounds(scale)); } | |
if (typeof translate !== 'undefined') { myZoom.translate(translate); } | |
if (typeof transition === 'undefined') { transition = maxAnimation*1.5; } | |
var transform = "translate("+( tooltipVisible ? myZoom.translate()[0] - 100 : myZoom.translate()[0] )+","+myZoom.translate()[1]+") scale("+myZoom.scale()+")"; | |
svg.transition().duration(transition).attr("transform",transform); | |
}; | |
var resetMap = function(){ | |
redrawMap([0,0],1); | |
}; | |
//////////////////////////////////////// | |
// Map Zooming | |
var myZoom = d3.behavior.zoom() | |
.scaleExtent([svgScaleMin, svgScaleMax]) | |
.on("zoomstart", function(){ | |
$target.addClass("is-Moving"); | |
}) | |
.on("zoom", function(){ | |
redrawMap(d3.event.translate,d3.event.scale,25); | |
}) | |
.on("zoomend", function(){ | |
$target.removeClass("is-Moving"); | |
}); | |
svgContainer.call(myZoom); | |
//////////////////////////////////////// | |
// Center Map on Country or Point | |
var centerMap = function(point) { | |
var $point = $(point); | |
if ( $point.length ) { | |
country = $point.data("country"); | |
svgBounds = $point[0].getBBox(); | |
} else { | |
country = point; | |
svgBounds = $("#"+country+"Markers")[0].getBBox(); | |
} | |
if ( svgBounds !== undefined ) { | |
var translate, | |
scale = 2.8; // ideal scale | |
scaleHoriz = ( mapScale / Math.abs(svgBounds.width + 10 ) )*scale; | |
scaleVert = ( mapScale / Math.abs(svgBounds.height + 10 ) )*scale; | |
scale = scaleBounds(Math.min(scaleHoriz,scaleVert)); | |
translate = [ | |
(mapCenterX - ((svgBounds.x + svgBounds.width/2) * scale)), | |
(mapCenterY - ((svgBounds.y + svgBounds.height/2) * scale)), | |
]; | |
redrawMap(translate,scale); | |
} else { | |
resetMap(); | |
} | |
}; | |
//////////////////////////////////////// | |
// Reveal Marker & Details on Locations List Click | |
$root.on("click","#Location-Listings .Address",function(){ | |
var id = $(this).data("id"); | |
highlightPoint("#Marker-"+id); | |
}); | |
}; | |
var RenderedMap = new LocationMap("#Locations-Map"); | |
locationsFetch.then(RenderedMap.buildMarkers); | |
$(window).resize( $.throttle(500, RenderedMap.resizeMap ) ); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment