Time Filter is a jquery plugin, using jquery ajax data service, and using d3.js to visualize data from a USGS geojson source. click the pencil icon to start the query. The timeline bars are based on the Brush and Zoom: http://bl.ocks.org/mbostock/34f08d5e11952a80609169b7917d4172 Also, the globe was added based on working here and : https://gist.github.com/powersparks/a8877d02050ada2f3ab0e9d82c6dd1c2
Last active
January 13, 2017 11:10
-
-
Save powersparks/f7c042e2203c8bea6c5d35b958202b27 to your computer and use it in GitHub Desktop.
D3.js Time Filter as a jQuery Plugin.
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
license: gpl-3.0 |
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
(function($){ | |
api = { | |
register:function (context){ | |
// api.renderGlobeCanvas(context.globeViewerId); | |
//api.renderGlobeSvg(context.globeViewerId); | |
}, | |
renderGlobeSvg:function(globeDivId){ | |
var diameter = 320, | |
radius = diameter/2, | |
velocity = .01, | |
then = Date.now(); | |
// Width and height of the SVG element | |
var width = 600, height = 300; | |
// Store the rotation and scale of the projection | |
var state = {x: 0, y: -45, scale: height / 2}; | |
var projection1 = d3.geoOrthographic() | |
.scale(radius - 2) | |
.translate([radius, radius]) | |
.clipAngle(90) | |
.precision(0); | |
var projection = d3.geoOrthographic() | |
.scale(state.scale) //.scale(height / 2) | |
.translate([radius, radius]) //.translate([width / 2, height / 2]) | |
.clipAngle(90) | |
.rotate([state.x, state.y]); | |
api.projection = projection; | |
var svg = d3.select("#" + globeDivId).append("svg") | |
.attr("width", diameter) | |
.attr("height", diameter); | |
var path = d3.geoPath() | |
.projection(projection); | |
var globe = {type: "Sphere"}; | |
svg.append("path") | |
.datum(globe) | |
.attr("class", "foreground") | |
.attr("d", path); | |
d3.json("world-110m.json", function(error, world) { | |
if (error) throw error; | |
var land = topojson.feature(world, world.objects.land), | |
globe = {type: "Sphere"}; | |
svg.insert("path") | |
.datum(topojson.feature(world, world.objects.land)) | |
.attr("class", "land") | |
.attr("d", path); | |
d3.timer(function() { | |
var angle = velocity * (Date.now() - then); | |
projection.rotate([angle,0,0]); | |
svg.selectAll("path") | |
.attr("d", path.projection(projection)); | |
}); | |
}); | |
$.coria.globe.svg = svg; | |
}, | |
renderGlobeCanvas: function(canvasId){ | |
var diameter = 960 / 3, | |
radius = diameter / 2, | |
velocity = 0.01; | |
var projection = d3.geoOrthographic() | |
.scale(radius - 2) | |
.translate([radius, radius]) | |
.clipAngle(90) | |
.precision(0); | |
d3.select("#" + canvasId ).selectAll(".title") | |
.data(["λ", "φ", "γ"]) | |
.enter().append("div") | |
.attr("class", "title") | |
.style("width", diameter + "px") | |
.text(function(d) { return d; }); | |
var canvas = d3.select("#" + canvasId ).selectAll("canvas") | |
.data(d3.range(3)) | |
.enter().append("canvas") | |
.datum(function() { return this.getContext("2d"); }) | |
.attr("width", diameter) | |
.attr("height", diameter); | |
var path = d3.geoPath() | |
.projection(projection); | |
d3.json("world-110m.json", function(error, world) { | |
if (error) throw error; | |
var land = topojson.feature(world, world.objects.land), | |
globe = {type: "Sphere"}; | |
d3.timer(function(elapsed) { | |
var angle = velocity * elapsed; | |
canvas.each(function(context, i) { | |
var rotate = [0, 0, 0]; | |
rotate[i] = angle, projection.rotate(rotate); | |
context.clearRect(0, 0, diameter, diameter); | |
context.beginPath(), path.context(context)(land), context.fill(); | |
context.beginPath(), path(globe), context.stroke(); | |
}); | |
}); | |
}); | |
} | |
}; | |
$.coria = $.coria ? $.coria : {}; | |
$.coria.globe = api; | |
})(jQuery); |
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
/// <reference path="d3.js" /> | |
/// <reference path="C:\Program Files\Telligent\9.2\Utility\JQuery\jquery.min.js" /> | |
/// <reference path="C:\Program Files\Telligent\9.2\Utility\JQuery\evolution\telligent.evolution.js" /> | |
var quakeResultsTemp ={ | |
"type": "FeatureCollection", | |
"metadata": { | |
"generated": 1481433099000, | |
"url": "http://earthquake.usgs.gov/fdsnws/event/1/query?callback=quake321705&format=geojson&starttime=2014-01-01&endtime=2014-01-02", | |
"title": "USGS Earthquakes", | |
"status": 200, | |
"api": "1.5.2", | |
"count": 277 | |
}, | |
"features": [{ | |
"type": "Feature", | |
"properties": { | |
"mag": 1.29, | |
"place": "10km SSW of Idyllwild, CA", | |
"time": 1388620296020,/****Times are reported in milliseconds since the epoch ( 1970-01-01T00:00:00.000Z)*/ | |
"updated": 1457728844428, | |
"tz": -480, | |
"url": "http://earthquake.usgs.gov/earthquakes/eventpage/ci11408890", | |
"detail": "http://earthquake.usgs.gov/fdsnws/event/1/query?eventid=ci11408890&format=geojson", | |
"felt": null, | |
"cdi": null, | |
"mmi": null, | |
"alert": null, | |
"status": "reviewed", | |
"tsunami": 0, | |
"sig": 26, | |
"net": "ci", | |
"code": "11408890", | |
"ids": ",ci11408890,", | |
"sources": ",ci,", | |
"types": ",cap,focal-mechanism,general-link,geoserve,nearby-cities,origin,phase-data,scitech-link,", | |
"nst": 39, | |
"dmin": 0.06729, | |
"rms": 0.09, | |
"gap": 51, | |
"magType": "ml", | |
"type": "earthquake", | |
"title": "M 1.3 - 10km SSW of Idyllwild, CA" | |
}, | |
"geometry": { | |
"type": "Point", | |
"coordinates": [-116.7776667, 33.6633333, 11.008] | |
}, | |
"id": "ci11408890" | |
}], | |
"bbox": [-179.688, -56.1218, -0.653, 167.8106, 64.8073, 582.05] | |
}; | |
(function ($) { | |
var date_dynField = "Date"; | |
var y_dynField = "price"; | |
var api = { | |
tfc_layout_array: [], | |
tfc_charts_array: [], | |
tfc_clipPath: function () { | |
var _id = "clip", _svg, _hgt, _wit, _shp = "rect"; | |
function clipPath(svg, width, height) { | |
if (!arguments.length) { | |
return { | |
id: _id, | |
svg: _svg, | |
height: _hgt, | |
width: _wit, | |
shape: _shp | |
}; | |
} | |
svg.append("defs").append("clipPath") | |
.attr("id", "clip") | |
.append("rect") | |
.attr("width", width) | |
.attr("height", height); | |
_id = "clip"; | |
_svg = svg; | |
_hgt = height; | |
_wit = width; | |
_shp = "rect"; | |
return clipPath(); | |
} | |
clipPath.id = function (value) { if (!arguments.length) { return _id; } _id = value; return clipPath; }; | |
clipPath.svg = function (value) { if (!arguments.length) { return _svg; } _svg = value; return clipPath; }; | |
clipPath.height = function (value) { if (!arguments.length) { return _hgt; } _hgt = value; return clipPath; }; | |
clipPath.width = function (value) { if (!arguments.length) { return _wit; } _wit = value; return clipPath; }; | |
clipPath.shape = function (value) { if (!arguments.length) { return _shp; } _shp = value; return clipPath; }; | |
return clipPath; | |
}, | |
tfc_layout: function () { | |
var _svgSelector = "", _svg, | |
_cntrl_width = 600, | |
_cntrl_height = 80, | |
_margin = { top: 35, right: 10, bottom: 35, left: 10 }, | |
_width = (_cntrl_width - _margin.left - _margin.right), | |
_height = (_cntrl_height - _margin.top - _margin.bottom), | |
_name = 'ChartName'; | |
//x = d3.scaleTime().range([0, _width]), | |
//y = d3.scaleLinear().range([_height, 0]), | |
//_xAxis = d3.axisTop(x).tickSize([_height]), | |
// _yAxis = d3.axisRight(y).ticks(0).tickSize(_width); | |
//x.domain([new Date().setFullYear(2016, 01, 01), Date.now()]); | |
//y.domain([0, 100]); | |
function tfc_layout(opts) { | |
if (!arguments.length) { | |
return { | |
chartName: _name, | |
svgSelector: _svgSelector, | |
svg: _svg, | |
cntrl_width: _cntrl_width, | |
cntrl_height: _cntrl_height, | |
width: _width, | |
height: _height, | |
margin: _margin | |
}; | |
} | |
_name = opts.name ? opts.name : _name; | |
_svgSelector = opts.svgSelector ? opts.svgSelector : _svgSelector; | |
_svg = opts.svg ? opts.svg : _svgSelector; | |
_cntrl_width = opts.width ? opts.width : _cntrl_width; | |
_cntrl_height = opts.height ? opts.height : _cntrl_height; | |
_margin = opts.margin ? opts.margin : _margin; | |
_margin.top = opts.margin ? opts.margin.top ? opts.margin.top : _margin.top : _margin.top; | |
_margin.bottom = opts.margin ? opts.margin.bottom ? opts.margin.bottom : _margin.bottom : _margin.bottom; | |
_margin.right = opts.margin ? opts.margin.right ?opts.margin.right : _margin.right : _margin.right ; | |
_margin.left = opts.margin ? opts.margin.left ? pts.margin.left : _margin.left : _margin.left; | |
} | |
tfc_layout.chartName = function (value) { if (!arguments.length) { return _name; } _name = value; return tfc_layout;}; | |
tfc_layout.svgSelector = function (value) { if (!arguments.length) { return _svgSelector; } _svgSelector = value; return tfc_layout; };// gets or sets selection string | |
tfc_layout.svg = function (value) { if (!arguments.length) { return _svg; } _svg = value; return tfc_layout; };//gets svg if selector is defined or sets (using selector string) | |
tfc_layout.cntrl_width = function (value) { if (!arguments.length) { return _cntrl_width; } _cntrl_width = value; _width = (_cntrl_width - _margin.right - _margin.left); return tfc_layout; };//get or set control width | |
tfc_layout.cntrl_height = function (value) { if (!arguments.length) { return _cntrl_height; } _cntrl_height = value; _height = (_cntrl_height - _margin.top - _margin.bottom); return tfc_layout; };//get or set control height | |
tfc_layout.width = function (value) { if (!arguments.length) { return _width; } _width = value; _cntrl_width = (_width + _margin.left + _margin.right); return tfc_layout; };//gets width based on margins or sets width, as a number calculated base on margins /*if (checkNum(value)) tfc_layout({ "width" : value, "height": (value * 0.02) }); return tfc_layout; }; | |
tfc_layout.height = function (value) { if (!arguments.length) { return _height; } _height = value; _cntrl_height = (_height + _margin.top + _margin.bottom); return tfc_layout; };//if (checkNum(value)) tfc_layout({ "height": value, "width" : (value / 0.02) }); return tfc_layout; }; | |
tfc_layout.margin = function (value) { if (!arguments.length) { return _margin; } _margin = value; return tfc_layout; };//margin | |
tfc_layout.margin.top = function (value) { if (!arguments.length) { return _margin.top; } _margin.top = value; _height = (_cntrl_height - _margin.top - _margin.bottom); return tfc_layout; };//top margin | |
tfc_layout.margin.bottom = function (value) { if (!arguments.length) { return _margin.bottom; } _margin.bottom = value; _height = (_cntrl_height - _margin.top - _margin.bottom); return tfc_layout; };//bottom margin | |
tfc_layout.margin.right = function (value) { if (!arguments.length) { return _margin.right; } _margin.right = value; _width = (_cntrl_width - _margin.left - _margin.right); return tfc_layout; };//right margin | |
tfc_layout.margin.left = function (value) { if (!arguments.length) { return _margin.left; } _margin.left = value; _width = (_cntrl_width - _margin.left - _margin.right); return tfc_layout; };//left margin | |
// tfc_layout.x = function (value) { if (!arguments.length) { return x; } x = value; return tfc_layout; };// x scales or sets range | |
//tfc_layout.y = function (value) { if (!arguments.length) { return y; } y = value; return tfc_layout; };// y scales or sets range | |
// tfc_layout.xAxis = _xAxis ;//d3.axisTop(x).tickSize([_height]);//function (value) { if (!arguments.length) { return _xAxis; } _xAxis = value; return tfc_layout; };// xAxis setup | |
//tfc_layout.yAxis = _yAxis ;//d3.axisRight(y).ticks(0).tickSize(_width);//function (value) { if (!arguments.length) { return _yAxis; } _yAxis = value; return tfc_layout; };// yAxis setup | |
// var _xAxis = d3.axisTop(x).tickSize([height]), | |
// _yAxis = d3.axisRight(y).ticks(0).tickSize(width); | |
return tfc_layout; | |
}, | |
tfc_chart: function () { | |
/** | |
* Class Name: Time Filter Control (servos) | |
* Description: TFC uses a paradigm of gears or virtual servo-mechanisms. | |
* using d3js, the objects consist of master and slave gear ratios, | |
* feedback, and adjustment surfaces( or pentiometers) | |
* to sense, move (rotate) and zoom on axis. | |
* 1) "Variable" - define layout dimensions (required as input) | |
* a) Height, width, margins required for svg control | |
* b) Use the default settings, or set your own | |
* c) Reusing and naming "tfc_layout" class | |
* d) Based on "Margin Convention" example: | |
* https://bl.ocks.org/mbostock/3019563 | |
* e) other examples that helped form this: | |
brush and zoom | |
http://bl.ocks.org/tnightingale/4718717 | |
*/ | |
// var tfc_layout = new $.coria.timeFilterControl.tfc_layout(); | |
var _timeFilterChart, _x, _y, _xAxis, _yAxis, X_MinFromDateExample, X_MaxToDateExample, Y_MinFromValueExample, Y_MinFromValueExample; | |
var _tfc_layout , _name; | |
function TimeFilterChart(tfc_layout,data) { | |
if (!arguments.length) { | |
return { | |
chartName: _name, | |
chart: _timeFilterChart, | |
x: _x, | |
y: _y, | |
xAxis: _xAxis, | |
yAxis: _yAxis, | |
settings: _tfc_layout | |
}; | |
} | |
/***** | |
* 2) "Algebra" - group and assemble what is broken | |
* a) Select the html | |
* b) Append svg control (or chart) | |
* c) set svg control's class name, width, & height | |
* d) Append svg group ("g") orgin within the control | |
*/ | |
_name = tfc_layout.chartName(); | |
_tfc_layout = tfc_layout; | |
//_timeFilterChart = d3.select("svg.timeline-svg-filter2") | |
_timeFilterChart = api.svgMain | |
.append("svg") | |
.attr("class", "time-filter-control") | |
.attr("width", _tfc_layout.cntrl_width()) | |
.attr("height", _tfc_layout.cntrl_height()) | |
.append("g") | |
.attr("class", "timeline-axis-container layout") | |
.attr("transform", "translate(" + _tfc_layout.margin.left() + "," + _tfc_layout.margin.top() + ")"); | |
/*** | |
* 5) "Scaling" - arrange layout to domains | |
* a) Scale "time" range along layout's x-axis | |
* b) Scale "linear" range along the layout's y-axis | |
*/ | |
_x = d3.scaleTime().range([0, _tfc_layout.width()]), | |
_y = d3.scaleLinear().range([_tfc_layout.height(), 0]); | |
/****** | |
* c) Scaling axis' ticks and tick length to layout, use axis orienation methods, e.g., "axisTop" or "axisRight" | |
* d) Methods instantiate | |
*/ | |
_xAxis = d3.axisTop(_x).tickSize(_tfc_layout.height()), | |
_yAxis = d3.axisRight(_y).ticks(0).tickSize(_tfc_layout.width()); | |
/**** | |
* f) Domain has Data Charateistics, EXAMPLES provided. | |
* e) Scaling Domain - overide d3 default domains on x and y axis | |
**/ | |
var X_MinFromDateExample = new Date().setFullYear(2016, 01, 01), X_MaxToDateExample = Date.now(); | |
var Y_MinFromValueExample = 0, Y_MaxToValueExample = 1000; | |
if (data == null) { | |
_x.domain([X_MinFromDateExample, X_MaxToDateExample]); | |
_y.domain([Y_MinFromValueExample, Y_MaxToValueExample]); | |
} else { | |
_x.domain(d3.extent(data, function (d) { return d[date_dynField]; })); | |
_y.domain([0, 100]); | |
// y.domain([0, d3.max(data, function (d) { return d.price; })]); | |
} | |
/**** | |
* h) Append "g" x-axis, class, add xAxis' translate ticks to layout | |
* i) Append "g" y-axis, class, add yAxis' | |
*/ | |
_timeFilterChart | |
.append("g") | |
.attr("class", "axis axis--x") | |
.attr("transform", "translate(0," + (_tfc_layout.height()) + ")") | |
.call(_xAxis); | |
_timeFilterChart | |
.append("g") | |
.attr("class", "axis axis--y") | |
.call(_yAxis); | |
/*** | |
chart: _timeFilterChart, | |
x: _x, | |
y: _y, | |
xAxis: _xAxis, | |
yAxis: _yAxis, | |
settings: _tfc_layout | |
*/ | |
//return TimeFilterChart;//_timeFilterChart; | |
} | |
TimeFilterChart.chart = function (value) { if (!arguments.length) { return TimeFilterChart; } _timeFilterChart = value; return TimeFilterChart; }; | |
TimeFilterChart.settings = function (value) { if (!arguments.length) { return _tfc_layout; } _tfc_layout = value; return TimeFilterChart; }; | |
TimeFilterChart.chartName = function (value) { if (!arguments.length) { return _name; } _name = value; return TimeFilterChart;}; | |
TimeFilterChart.x = function (value) { if (!arguments.length) { return _x; } _x = value; return TimeFilterChart; }; | |
TimeFilterChart.y = function (value) { if (!arguments.length) { return _y; } _y = value; return TimeFilterChart; }; | |
TimeFilterChart.xAxis = function (value) { if (!arguments.length) { return _xAxis; } _xAxis = value; return TimeFilterChart; }; | |
TimeFilterChart.yAxis = function (value) { if (!arguments.length) { return _yAxis; } _yAxis = value; return TimeFilterChart; }; | |
TimeFilterChart.settings = function (value) { if (!arguments.length) { return _tfc_layout; } _tfc_layout = value; return TimeFilterChart; }; | |
return TimeFilterChart; | |
}, | |
tfc_data: function (d) { | |
opts.data = d; | |
api.tfc_setup(opts); | |
}, | |
tfc_addEditIconBtn: function (opts) { | |
d3.selectAll('svg'+ ">*").remove(); | |
var iconId = opts.timelineEditImgId, btnId = opts.timelineEditBtnId, url = opts.timelineEditSvgUrl; | |
$('#' + iconId).attr("src", url); | |
$("span.ui-loading").hide(); | |
$('#' + btnId).on('click', function () { | |
$("span.ui-loading").show(); | |
/***** | |
* <option value="internalSearch">REST API Search</option> | |
* <option value="earthquakes">GeoJson USGS Earthquakes</option> | |
**/ | |
var DataChoice = $('#' +opts.tfc_sourceId).val(); | |
switch (DataChoice) { | |
case "restApi": | |
//console.info("rest api"); | |
tfc_dataRestApi(opts); | |
break; | |
case "csvFile": | |
tfc_dataCsvFie(opts); | |
break; | |
case "jsonUrl_earthquakes": | |
//console.info("case jsonUrl_earthquakes"); | |
tfc_dataJsonUrl(opts); | |
break; | |
default: | |
tfc_dataRestApi(opts); | |
break; | |
} | |
function tfc_dataRestApi(opts) { | |
var search = api.tfc_data_SearchTelligentRestCall(); | |
search(function (results) { | |
opts.data = results.SearchResults.map(function (d) { | |
return type(d, "Date", "Rating"); | |
}); | |
api.tfc_setup(opts ); | |
$("span.ui-loading").hide(); | |
}); | |
} | |
function tfc_dataCsvFile(opts) { } | |
function tfc_dataJsonUrl(opts) { | |
var quakeRequest = api.tfc_data_UsgsEarthquake(); | |
var startDateText= opts.tfc_fromDateId ? $('#' + opts.tfc_fromDateId).val() : "2014-01-01", | |
endDateText = opts.tfc_toDateId ? $('#' + opts.tfc_toDateId).val() : "2014-01-02"; | |
quakeRequest(function (results, startDateTime, endDateTime ) { | |
opts.data = results.features.map(function (d) { | |
d.Date = new Date(d.properties.time); | |
d.price = d.properties.mag; | |
return d; | |
}); | |
// console.info("quakeRequest"); | |
// console.info(results); | |
opts.geoData = results; | |
api.tfc_setup(opts); | |
$("span.ui-loading").hide(); | |
}, startDateText, endDateText); | |
} | |
}); | |
}, | |
tfc_setup: function (opts) { | |
var data = opts.data ? opts.data : null; | |
var svgMainHeight = 80; | |
d3.selectAll('svg' + ">*").remove(); | |
d3.selectAll('#' + opts.globeViewerId + ">*").remove(); | |
$.coria.globe.renderGlobeSvg(opts.globeViewerId); | |
// create path | |
var path = d3.geoPath() | |
.projection($.coria.globe.projection); | |
// Create the graticule lines and append them to the SVG container | |
// | |
// | |
var graticule = d3.geoGraticule(); | |
// Add the graticule to the figure | |
$.coria.globe.svg.append('path').datum(graticule()) | |
.attr('class', 'graticule') | |
.attr('d', path) | |
.style('fill','none') | |
.style('stroke','#aaa') | |
.style('stroke-width','1px') | |
.style('stroke-opacity','0.5') | |
.style('pointer-events','none'); | |
// Magnitude extent of quakes | |
/****setup globe end**/ | |
api.svgMain = d3.selectAll('#' + opts.tfcSvgId); | |
api.svgMain.attr("viewBox", "0 0 600 " + svgMainHeight); | |
//new layout and main time chart setup | |
var _tfc_layout_slave = new $.coria.timeFilterControl.tfc_layout() | |
.margin.top(10) | |
.margin.bottom(60) | |
.margin.left(30) | |
.margin.right(30); | |
_tfc_layout_slave.chartName("SlavedChart"); | |
var _slaveChart = new $.coria.timeFilterControl.tfc_chart(); | |
_slaveChart(_tfc_layout_slave, data); | |
var clipPath = api.tfc_clipPath(); | |
var clipRect = clipPath(api.svgMain, _tfc_layout_slave.width(), _tfc_layout_slave.height()); | |
api.clipRect = clipRect; | |
var line = d3.line() | |
.defined(function (d) { return d; }) | |
.x(function (d) { return x(d[date_dynField]); }) | |
//.y(y(100)); | |
.y(function (d) { return y(d[y_dynField]); }); | |
if (data != null) { | |
_slaveChart().chart.selectAll("circle") | |
.data(data).enter().append("circle") | |
.attr("clip-path", "url('#clip')") | |
.attr("d", line) | |
.attr("class", "dot") | |
.attr("r", 3.5) | |
.attr("opacity", 0.7) | |
.style("fill", "steelblue"); | |
/* $.coria.globe.svg.selectAll("circle") | |
.data(data).enter().append("circle") | |
.attr("class", "quake") | |
.attr("r", 1.5) | |
.attr("opacity", 0.5) | |
.style("fill", "steelblue"); | |
.insert("path") | |
.datum(topojson.feature(quake, quake.objects.land)) | |
.attr("d", path); | |
*/ | |
} | |
api.tfc_charts_array.push( _slaveChart ); | |
var _dist_between_charts = _tfc_layout_slave.cntrl_height(); | |
//new layout and master time chart setup | |
var _tfc_layout_master = new $.coria.timeFilterControl.tfc_layout() | |
.margin.top(35) | |
.margin.bottom(35) | |
.margin.left(30) | |
.margin.right(30); | |
_tfc_layout_master.chartName("MasteredChart"); | |
api.tfc_layout_array.push(_tfc_layout_master); | |
var _masterChart = new $.coria.timeFilterControl.tfc_chart(); | |
_masterChart(_tfc_layout_master,data); | |
api.tfc_charts_array.push( _masterChart ); | |
var masterChart = _masterChart; | |
var slaveChart = _slaveChart; | |
/** | |
* 6) Brushes (master & slave) to interact between time filter charts | |
* a) Setup brush on X axis, | |
* b) extend of rendered brush | |
* c) event method on "brush" and "end" | |
*/ | |
//Setup Brush for slave time filter | |
var brush_sensor_slave = d3.brushX() | |
.extent([[0, 0], [_tfc_layout_slave().width, _tfc_layout_slave().height]]) | |
.on("brush end", slave_feedback); | |
//Append brush to slave chart | |
_slaveChart().chart | |
.append("g") | |
.attr("class", "brush brush-sensor brush-sensor-slave") | |
.call(brush_sensor_slave) | |
.call(brush_sensor_slave.move, [_tfc_layout_slave().width - Math.floor(_tfc_layout_slave().width / 2) - (_tfc_layout_slave().width / 5), _tfc_layout_slave().width - Math.floor(_tfc_layout_slave().width / 2) + (_tfc_layout_slave().width / 5)]); | |
/* | |
*[width - Math.floor(width / 2) - (width / 5), width - Math.floor(width / 2) + (width / 5)] | |
* Sets up a zoom servo as an adjustment surface for changing Gear(master/slave) Ratio | |
* uses a lazy setting, "Infinity" which assumes a level of precision in our data that is not realistic. | |
* realistic would calculate data resolution, e.g. date/time may only go to seconds, days, weeks, etc | |
* Spatial, temporal, or aspatial data resolutions need to be reported back to user in a meaningful anology | |
*/ | |
var zoom_ratio = d3.zoom() | |
.scaleExtent([1, Infinity]) | |
.translateExtent([[0, 0], [_tfc_layout_slave().width, _tfc_layout_slave().height]]) | |
.extent([[0, 0], [_tfc_layout_slave().width, _tfc_layout_slave().height]]) | |
.on("zoom", slave_feedback); | |
//append zoom servo | |
_slaveChart().chart | |
.append("rect") | |
.attr("class", "zoom servo ratio-adjustment-surface") | |
.attr("width", _tfc_layout_slave().width) | |
.attr("height", _tfc_layout_slave().height) | |
.attr("transform", "translate(" + 0 + "," + Math.floor(_tfc_layout_slave().margin.top / 2) + ")") | |
.call(zoom_ratio); | |
if(opts.geoData != null && opts.geoData.features != null ) | |
{ | |
var magExtent = d3.extent(opts.geoData.features, function(d) { | |
return d.properties.mag; | |
}); | |
var rScale = d3.scaleLinear() | |
.domain(magExtent) | |
.range([d3.max(magExtent), d3.min(magExtent)]); | |
path.pointRadius(function(d) { | |
return d.properties ? rScale(d.properties.mag) : 1; | |
}); | |
var s = [_tfc_layout_slave().width - Math.floor(_tfc_layout_slave().width / 2) - (_tfc_layout_slave().width / 5), _tfc_layout_slave().width - Math.floor(_tfc_layout_slave().width / 2) + (_tfc_layout_slave().width / 5)]; | |
var slaveFeedbackRange = s.map(_slaveChart().x.invert, _slaveChart().x); | |
var filterData = opts.geoData.features.filter(function(d){ return d.Date > slaveFeedbackRange[0] && d.Date < slaveFeedbackRange[1]; }); | |
$.coria.globe.svg.selectAll('path.quake-black').data(filterData) | |
.enter().append('path') | |
.attr('class', 'quake-black') | |
.attr('d', path); | |
} | |
//method for brushing event | |
function slave_feedback() { | |
if (d3.event.sourceEvent && (d3.event.sourceEvent.type === "wheel" || d3.event.sourceEvent.type === "zoom")) { | |
//change resolution of axis and data on slave chart, slave chart brush, and master chart brush | |
//console.info("zoom"); | |
var transform = d3.event.transform; | |
_slaveChart().x.domain(transform.rescaleX(_masterChart().x).domain()); | |
_slaveChart().chart.select(".axis--x").call(_slaveChart().xAxis); | |
_masterChart().chart.select(".brush").call(brush_sensor_master.move, _slaveChart().x.range().map(transform.invertX, transform)); | |
/**/ | |
} | |
if (d3.event.sourceEvent && (d3.event.sourceEvent.type === "wheel" || d3.event.sourceEvent.type === "zoom" || d3.event.sourceEvent.type === "mousemove" || d3.event.sourceEvent.type === "touchmove")) { | |
_slaveChart().chart.selectAll(".dot") | |
.attr('cx', function (d) { return _slaveChart().x(d[date_dynField]); }) | |
.attr('cy', _tfc_layout_slave.height() * 0.5); | |
var s = d3.event.selection || _slaveChart().x.range(); | |
var slaveFeedbackRange = s.map(_slaveChart().x.invert, _slaveChart().x); | |
// Draw the quakes on the map if data is not null | |
var filterData = opts.geoData.features.filter(function(d){ | |
return d.Date > slaveFeedbackRange[0] && d.Date < slaveFeedbackRange[1]; | |
}); | |
$.coria.globe.svg.selectAll('path.quake-black') | |
.remove().exit(); | |
$.coria.globe.svg.selectAll('path.quake-black') | |
.data(filterData) | |
.enter().append('path') | |
.attr('class', 'quake-black') | |
.attr('d', path); | |
} | |
} | |
//Setup Brush sensor for master chart's time filter | |
var brush_sensor_master = d3.brushX() | |
.extent([[0, 0], [_tfc_layout_master.width(), _tfc_layout_master.height()]]) | |
.on("brush end", master_feedback); | |
// Append brush to master chart | |
_masterChart().chart | |
.append("g") | |
.attr("class", "brush brush-sensor brush-sensor-master") | |
.call(brush_sensor_master) | |
.call(brush_sensor_master.move, _masterChart().x.range()); | |
//method for brushing event | |
function master_feedback() { | |
if (d3.event.sourceEvent && (d3.event.sourceEvent.type === "mousemove" || d3.event.sourceEvent.type === "touchmove")) { | |
//array from brush sensor or use default x-axis range | |
var sensor = d3.event.selection || _masterChart().x.range(); | |
//provide feedback from master to slave | |
//drive domain mapping sensor output over master x-min and max values | |
_slaveChart().x.domain(sensor.map(_masterChart().x.invert, _masterChart().x)); | |
//rescale the x axis after setting the domain. | |
_slaveChart().chart.select(".axis--x").call(_slaveChart().xAxis); | |
_slaveChart().chart.select(".zoom").call(zoom_ratio.transform, d3.zoomIdentity | |
.scale(_tfc_layout_slave().width / (sensor[1] - sensor[0])) | |
.translate(-sensor[0], 0)); | |
} | |
_slaveChart().chart.selectAll(".dot") | |
.attr('cx', function (d) { return _slaveChart().x(d[date_dynField]); }) | |
.attr('cy', _tfc_layout_slave.height() * 0.5); | |
} | |
},/****tfc_setup end**/ | |
tfc_data_UsgsEarthquake: function () { | |
//https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&starttime=2014-01-01&endtime=2014-01-02&callback=test | |
//https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&starttime=2014-01-01&endtime=2014-01-02&callback=quake558755&_=1481418159231 | |
var _baseUrl = "https://earthquake.usgs.gov/fdsnws/event/1/query", | |
x3rand = Math.floor(Math.random() * 1000 + 0), | |
y3rand = Math.floor(Math.random() * 1000 + 0), | |
callbackname = "quake" + x3rand + y3rand, | |
startDateTime = "2014-01-01", | |
endDateTime = "2014-01-02"; | |
function quakeResult(callback, startDate, endDate) { | |
//console.info("ajax"); | |
$.ajax({ | |
url: _baseUrl, | |
dataType: "jsonp", | |
jsonpCallback: callbackname, | |
data: { | |
format: "geojson", | |
starttime: startDate ? startDate : startDateTime, | |
endtime: endDate ? endDate : endDateTime | |
}, | |
dataType: 'jsonp', | |
cache: true, | |
success: function (response) { | |
if (typeof callback !== 'undefined' && typeof callback === 'function') { | |
callback(response); | |
} | |
}, | |
error: function (state, status, message) { | |
console.error("fail: "); | |
console.error(state); | |
console.error(status); | |
console.error(message); | |
} | |
}); | |
}; | |
return quakeResult; | |
}, | |
tfc_data_SearchTelligentRestCall: function () { | |
var _baseUrl = $.telligent.evolution.site.getBaseUrl(true), | |
_searchRestApi = "api.ashx/v2/search.json", | |
x3rand = Math.floor(Math.random() * 1000 + 0), | |
y3rand = Math.floor(Math.random() * 1000 + 0), | |
callbackname = "search" + x3rand + y3rand; | |
//var loading = $.telligent.evolution.ui.components.loading = { | |
// setup: function () { | |
// // setup is called once per page if at least one element matches this component | |
// }, add: function (elm, options) { | |
// // add is called for each unique instance of an element matching the component | |
// // elm is the matching element | |
// // options is an object containing all data attributes defined on the element | |
// //$(elm).html('loading: ' + options.make + ' ' + options.model); | |
// } | |
//}; | |
// Query: "{!geofilt pt=21.5,-158 sfield=GeoTagGeoHash d=10}" | |
var searchResult = function (callback) { | |
$.telligent.evolution.get({ | |
url: _baseUrl + _searchRestApi, | |
callback: callbackname, | |
data: { | |
Query: "*:*" | |
}, | |
cache: false | |
}).done(function (r) { | |
if (typeof callback !== 'undefined' && typeof callback === 'function') { | |
callback(r); | |
} | |
}); | |
}; | |
return searchResult; | |
}, | |
getLayouts: function () { return api.tfc_layout_array; } , | |
register: function (opts) { | |
if (typeof d3 === 'undefined') return; | |
opts.toDate = new Date(Date.now()); | |
opts.fromDate = new Date(Date.now() - 1*24*60*60*1000); | |
var fromDate = TheDate(); | |
fromDate(opts.fromDate); | |
var toDate = TheDate(); | |
toDate(opts.toDate); | |
$('#' + opts.tfc_fromDateId).val(toDate.is()); | |
$('#' + opts.tfc_toDateId).val(toDate.is()); | |
$('#' + opts.tfc_fromDateId).val(fromDate.is()); | |
api.tfc_addEditIconBtn(opts); | |
api.tfc_setup(opts); | |
}, | |
TheDate: TheDate | |
}; | |
$.coria = $.coria ? $.coria : {}; | |
$.coria.timeFilterControl = api; | |
function checkMargins(margin) { | |
if (checkNum(margin.top) && checkNum(margin.bottom) && checkNum(margin.right) && checkNum(margin.left)) | |
return true; | |
return false; | |
} | |
function checkNum(value) { | |
if (isNaN(value)) { | |
throw new Error("value must be a number not: " + value); | |
} | |
return true; | |
} | |
/** | |
*type: convert date | |
*/ | |
function type(d, x_dynField, y_dynField) { | |
var parseDate = d3.utcParse("%Y-%m-%dT%H:%M:%S%Z"); | |
var value_dynField = y_dynField ? y_dynField : "price"; | |
var date_dynField = x_dynField ? x_dynField : "date"; | |
d[date_dynField] = parseDate(d[date_dynField]); | |
//hard coded y value... | |
var num = isNaN(d[y_dynField]) ? Math.floor(Math.random() * 1000) : d[y_dynField] * 100; | |
d[y_dynField] = num <1000 ? num : 1000 ;//d.price; | |
return d; | |
} | |
function TheDate() { | |
var _now, _day, _month, _year, _theDate; | |
function TheDate(date) { | |
if (!arguments.length) { | |
return { | |
dd_text: _day, | |
mm_text: _month, | |
yyyy_text: _year, | |
is: _theDate | |
}; | |
} | |
_now = new Date(date); | |
_day = ("0" + _now.getDate()).slice(-2); | |
_month = ("0" + (_now.getMonth() + 1)).slice(-2); | |
_year = _now.getFullYear() + ""; | |
_theDate = _year + "-" + (_month) + "-" + (_day); | |
return TheDate; | |
} | |
TheDate.dd_text = function (value) { if (!arguments.length) { return _day; } _day = value; return TheDate; }; | |
TheDate.mm_text = function (value) { if (!arguments.length) { return _month; } _month = value; return TheDate; }; | |
TheDate.yyyy_text = function (value) { if (!arguments.length) { return _year; } _year = value; return TheDate; }; | |
TheDate.is = function (value) { if (!arguments.length) { return _theDate; } _theDate = value; return TheDate; }; | |
return TheDate; | |
} | |
})(jQuery); |
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
.timeline-div svg{ | |
/* these are settings important for mapping svgs | |
position: absolute; | |
-webkit-box-sizing: unset ; | |
-moz-box-sizing: unset; | |
box-sizing: unset ; | |
shape-rendering: crispEdges | |
*/ | |
} | |
.axis--x .domain, .axis--grid .tick line { | |
stroke: #000; | |
} | |
.axis--x, .axis--y, .tick, .domain, .axis--grid .tick { | |
shape-rendering: crispEdges; | |
border: thin; | |
border-color:black; | |
} | |
.brush .brush-sensor { | |
shape-rendering: crispEdges; | |
border: thin; | |
border-color:black; | |
} | |
.brush .handle{ | |
shape-rendering: crispEdges; | |
fill: steelblue; | |
} | |
.timeline-container{ margin: 10px;} | |
.timeline-container-toolbar { | |
/*border: 2px dashed #444;*/ | |
height: 40px; | |
text-align: justify; | |
-ms-text-justify: distribute-all-lines; | |
text-justify: distribute-all-lines; | |
/* just for demo */ | |
min-width: 300px; | |
} | |
.timeline-container-toolbar:after { | |
content: ''; | |
width: 100%; | |
display: inline-block; | |
font-size: 0; | |
line-height: 0 | |
} | |
.timeline-container-toolbar2 { | |
position:relative; | |
top:8px; | |
/*width:100%;*/ | |
min-width: 300px; | |
height:40px; | |
padding:2px; | |
/***align example: //stackoverflow.com/questions/6865194/fluid-width-with-equally-spaced-divs/6880421#6880421*/ | |
border: 2px dashed #444; | |
text-align: justify; | |
-ms-text-justify: distribute-all-lines; | |
text-justify: distribute-all-lines; | |
} | |
/***align example: //stackoverflow.com/questions/6865194/fluid-width-with-equally-spaced-divs/6880421#6880421*/ | |
.timeline-container-toolbar > div { | |
/*width: 30px;*/ | |
height: 40px; | |
vertical-align: top; | |
display: inline-block; | |
*display: inline; | |
zoom: 1 ; | |
padding:5px; | |
} | |
.graticule { | |
fill: none; | |
stroke: #aaa; | |
stroke-width: 1px; | |
stroke-opacity: 0.5; | |
pointer-events: none; | |
} | |
.foreground { | |
fill: #d8ffff; | |
stroke: #333; | |
stroke-width: 1.5px; | |
} | |
.land { | |
fill: #d7c7ad; | |
stroke: #766951; | |
} | |
/*#stretch { | |
width: 100%; | |
display: inline-block; | |
font-size: 0; | |
line-height: 0 | |
} | |
#box1, #box3 { | |
background: #ccc | |
} | |
#box2, #box4 { | |
background: #0ff | |
}*/ | |
.ui-loading{ | |
/**float:right;*/ | |
} | |
.timeline-edit { | |
width:20px; | |
/*float: right;*/ | |
} | |
input.timeline-search { | |
border-radius:6px; | |
width:auto; | |
padding: 0 0 0 30px; | |
border:thin; | |
background-color:rgba(255, 255, 255, 0.3); | |
margin-left:4px; | |
} | |
input.timeline-search:hover{ | |
background-color:rgba(255, 255, 255, 0.5); | |
transition: all 300ms; | |
} | |
input.timeline-search:focus{ | |
background-color:rgba(255, 255, 255, 0.7); | |
transition: all 300ms; | |
} | |
.field-item-input.timeline-search::after { | |
font-size: 15px; | |
height: 15px; | |
width: 15px; | |
display:block; | |
font-family: Entypo; | |
content: "\e803"; | |
position: absolute; | |
color: #bdc3c7; | |
top: .1em; | |
left: 8px; | |
} | |
.timeline-div{ | |
background-color:rgba(130, 130, 130, 0.3); | |
border-radius: 12px; | |
border:none; | |
box-shadow: 3px 3px 5px rgba(0, 0, 0, 0.3); | |
margin:3px; | |
/* | |
font-size: 50px; | |
text-align: center; | |
line-height: 100px;*/ | |
/* height:100px; | |
width: 820px; | |
min-height:20px; | |
min-width:400px; | |
*/ | |
} | |
.line{ | |
border: solid 1px steelblue; | |
margin: 4px; | |
padding: 4px; | |
background-color: #eeeeec; | |
fill: none; | |
/* clip-path: url("/~powersparks/bz.html#clip"); | |
*/ | |
} | |
/** | |
g .brush > .overlay{ | |
stroke: #000 !important; | |
border: solid 1px #000; | |
} | |
***/ | |
/** | |
.brush .overlay{ | |
border: solid 1px steelblue; | |
fill:rgba(70,130,180,0.5); | |
} | |
*/ | |
.area { | |
fill: steelblue; | |
/* clip-path: url("/~powersparks/bz.html#clip");*/ | |
} | |
rect.zoom{ | |
cursor:ns-resize; | |
} | |
.zoom { | |
cursor: move; | |
fill: none; | |
pointer-events: all; | |
} | |
.context { | |
} | |
.axis--grid .domain { | |
fill: #ddd; | |
stroke: none; | |
} | |
.axis--x .domain, | |
.axis--grid .tick line { | |
stroke: #fff; | |
} | |
.axis--grid .tick--minor line { | |
stroke-opacity: .5; | |
} | |
.quake-black{ | |
stroke:red; | |
fill:red; | |
opacity: .3; | |
} | |
/* | |
<div id="container-glass-id"> | |
<img id="img-glass-id" src="http://lorempixel.com/450/300/sports" /> | |
<div id="overlay-glass-id">chipChocolate.py</div> | |
<svg id="svg-glass-id" width="450" height="100" viewBox="0 0 450 100" style="position: absolute; left:0;top: 0;"> | |
<defs> | |
<filter id="blur-glass-id"> | |
<feGaussianBlur in="SourceGraphic" stdDeviation="3" /> | |
</filter> | |
</defs> | |
<image id="image-glass-id" filter="url(#blur)" xlink:href="http://lorempixel.com/450/300/sports" x="0" y="0" height="300px" width="450px" /> | |
</svg> | |
</div> | |
*/ | |
/* | |
#svg-glass-id{} | |
#blur-glass-id{} | |
#container-glass-id { | |
position: relative; | |
width: 450px; | |
margin: 0 auto; | |
} | |
#img-glass-id { | |
height: 300px; | |
} | |
#overlay-glass-id { | |
position: absolute; | |
left: 0; | |
top: 0; | |
width: 100%; | |
z-index: 1; | |
color: rgba(188, 143, 143, 0.2); | |
font-size: 50px; | |
text-align: center; | |
line-height: 100px; | |
box-shadow: 0 3px 5px rgba(0, 0, 0, 0.3); | |
} | |
.container{ | |
width:auto;; | |
height:auto; | |
background-color: papayawhip; | |
box-sizing: border-box; | |
} | |
svg .responsive-graph | |
{ | |
width:960px; | |
height:500px; | |
} | |
.axis--grid .domain { | |
fill: #ddd; | |
stroke: none; | |
} | |
.axis--x .domain, | |
.axis--grid .tick line { | |
stroke: #fff; | |
} | |
.axis--grid .tick--minor line { | |
stroke-opacity: .5; | |
} | |
*/ |
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
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<title>Coria Timeline - Time Filter</title> | |
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js" ></script> | |
<script type="text/javascript" src='https://d3js.org/d3.v4.js' charset="utf-8"></script> | |
<script type="text/javascript" src="https://d3js.org/topojson.v1.min.js" ></script> | |
</head> | |
<body> | |
<link href='CoriaTimeline.css' rel='stylesheet' type='text/css'> | |
<div id="timelineContainerDivId" class="timeline-container"> | |
<div id="timelineId" class="timeline-div" > | |
<div id="timelineContainerToolBar" style="{display:none;}" class="timeline-container-toolbar"> | |
<div> | |
<input id="timelineSearch" style="{display:none;}" class="ui-tourtip timeline-search " autocomplete="off" placeholder="Search" value="" data-tourtipkey="1234" data-tourtipmessage="Add text to filter timeline posts" type="search"> | |
</div> | |
<div> | |
<select id="sourceId"> | |
<option value="jsonUrl_earthquakes">GeoJson USGS Earthquakes</option> | |
<option value="restApi">custom REST API Search</option> | |
</select> | |
</div> | |
<div> | |
<span class="ui-loading" style="{display:none; }" data-width="30" data-height="30"></span> | |
</div> | |
<div > | |
<span style="{ white-space:nowrap}"> <label for="fromDateId">From:</label> | |
<input id="fromDateId" type="date" /> </span> | |
</div> | |
<div> | |
<span style="{ white-space:nowrap}"><label for="toDateId">To:</label> | |
<input id="toDateId" type="date" /> </span> | |
</div> | |
<div> | |
<a href="#" id="timelineEditBtnId" style="{display:none;}" class="timeline-edit-btn"> | |
<img id="timelineEditImgId" src="" style="{display:none;}" class="timeline-edit" alt="timeline edit tool"/> | |
</a> | |
</div> | |
</div> | |
<div> | |
<svg id="tfcSvgId" class="timeline-svg-filter2" viewBox="0 0 600 80" preserveAspectRatio="xMidYMid"></svg> | |
</div> | |
</div> | |
</div> | |
<div id="globeViewerId" height="300" width="600"></div> | |
<script type="text/javascript" src="CoriaTimeFilter.js"></script> | |
<script type="text/javascript" src="CoriaGlobe.js"></script> | |
<script type="text/javascript"> | |
jQuery(function (j){ | |
j.coria.timeFilterControl.register({ | |
tfc_toDateId: "toDateId", | |
tfc_fromDateId: "fromDateId" , | |
tfc_sourceId: "sourceId", | |
tfc_clipPathId: "clip", | |
tfcSvgId: "tfcSvgId", | |
timelineEditBtnId:"timelineEditBtnId", | |
timelineEditSvgUrl: "edit.svg", | |
timelineEditImgId:"timelineEditImgId", | |
globeViewerId:"globeViewerId" | |
}); | |
}); | |
jQuery(function (j){ | |
j.coria.globe.register({ | |
globeViewerId:"globeViewerId" | |
}); | |
}); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment