First check in of map of science visualization

This commit is contained in:
kongchinhua 2011-05-19 15:16:56 +00:00
parent c365f9a39a
commit e6ea91b8be
232 changed files with 1853 additions and 0 deletions

View file

@ -0,0 +1,65 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
/*
* Simple JavaScript Inheritance
* By John Resig http://ejohn.org/
* MIT Licensed.
*/
// Inspired by base2 and Prototype
(function(){
var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
// The base Class implementation (does nothing)
this.Class = function(){};
// Create a new Class that inherits from this class
Class.extend = function(prop) {
var _super = this.prototype;
// Instantiate a base class (but only create the instance,
// don't run the init constructor)
initializing = true;
var prototype = new this();
initializing = false;
// Copy the properties over onto the new prototype
for (var name in prop) {
// Check if we're overwriting an existing function
prototype[name] = typeof prop[name] == "function" &&
typeof _super[name] == "function" && fnTest.test(prop[name]) ?
(function(name, fn){
return function() {
var tmp = this._super;
// Add a new ._super() method that is the same method
// but on the super-class
this._super = _super[name];
// The method only need to be bound temporarily, so we
// remove it when we're done executing
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, prop[name]) :
prop[name];
}
// The dummy class constructor
function Class() {
// All construction is actually done in the init method
if ( !initializing && this.init )
this.init.apply(this, arguments);
}
// Populate our constructed prototype object
Class.prototype = prototype;
// Enforce the constructor to be what we expect
Class.constructor = Class;
// And make this class extendable
Class.extend = arguments.callee;
return Class;
};
})();

View file

@ -0,0 +1,51 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
var ColorStrategy = Class.extend ({
getColor: function(key) {
}
});
var SingleColorStrategy = ColorStrategy.extend ({
init: function(defaultColor) {
this.defaultColor = '#000000';
if (defaultColor) {
this.defaultColor = defaultColor;
}
},
getColor: function(key) {
return this.defaultColor;
}
});
var DisciplineColorStrategy = SingleColorStrategy.extend ({
init: function(defaultColor) {
this._super(defaultColor);
},
getColor: function(key) {
var color = DISCIPLINES[key].color;
if (color) {
return color;
} else {
return this._super(key);
}
}
});
var SubdisciplineColorStrategy = ColorStrategy.extend ({
init: function(defaultColor) {
this.colorStrategy = new DisciplineColorStrategy(defaultColor);
},
getColor: function(key) {
var mapKey = SUBDISCIPLINES[key].discipline;
return this.colorStrategy.getColor(mapKey);
}
});
// Todo: Stop coding until comparison view, might need to use Temporal Line Graph solution
var AutoAssignColorStrategy = SingleColorStrategy.extend ({
init: function(defaultColor, ColorList) {
this._super(defaultColor);
},
getColor: function(key) {
}
});

View file

@ -0,0 +1,36 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
var ScinodePolygon = Polygon.extend({
init: function(options) {
options.polygon = createGoogleCirclePolygon(options);
this._super(options);
},
setValue: function(value) {
this.options.value = value;
},
getValue: function() {
return this.options.value;
},
setSize: function(size) {
this.polygon.setRadius(size);
this.setZIndex(-size);
}
});
function createScinodeMarker(map, label, value, radius, color, latlng) {
var circleOptions = {
label: label,
value: value,
strokeColor: color,
strokeOpacity: 1.0,
strokeWeight: 1.0,
fillColor: color,
fillOpacity: 0.25,
map: map,
center: latlng,
zIndex: -radius,
radius: radius // min: 10000, max: 2500000
};
return new ScinodePolygon(circleOptions);
}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,50 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
/**
* Manage async download contents and make sure don't have the same
* download request in the same time. Provide ability to abort download.
*/
var DownloadManager = Class.extend({
init: function() {
this.downloadList = {};
},
download: function(url, success) {
if (!this.hasKey(url)) {
this.downloadList[url] = { success: success, // TODO Try removing this property
jqxhr: this.startDownload(url, success, this.downloadList)};
}
},
downloadAndWait: function(url, success) {
if (!this.hasKey(url)) {
$.ajax({
url: url,
async: false,
dataType: 'json',
success: function(countData) { success(countData); }
});
}
},
startDownload: function(url, success, downloadList) {
return $.getJSON(url, // TODO Not always "latest" //TODO Test on server with big file that consume 3 seconds download time. Then Keep on and off the checkbox while downloading, to verify if the duplicate happens
function(countData) {
if (success) {
success(countData);
}
delete(downloadList[url]);
}
);
},
hasKey: function(url) {
return (this.downloadList[url]);
},
abort: function(url) {
var options = this.downloadList[url];
if (options) {
options.jqxhr.abort();
delete(this.downloadList[url]);
}
},
isDone: function(url) {
return !this.hasKey(url);
}
});

View file

@ -0,0 +1,55 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
/**
* This is the Google MAP API utilities file to make sure all calls to
* Google API is correct. Please add all common utilities at here.
*/
var GMAPS = google.maps;
var GEVENT = google.maps.event;
var DEFAULT_POINT = new google.maps.Point(0, 0);
function createMarkerImage(url, width, height) {
return new GMAPS.MarkerImage(
url,
new GMAPS.Size(width, height), /* set the image viewable window size */
DEFAULT_POINT, /* Use this to cut the image */
// TODO Fix icons so their center at the right spot. Very low priority.
new google.maps.Point(width/2, height/2), /* use this to shift the marker location in pixels. Default */
new GMAPS.Size(width, height)); /* set the desired image size */
}
function createNoWrapLatLng(lat, lng) {
return new GMAPS.LatLng(lat, lng, true);
}
function createGoogleCirclePolygon(options) {
return new GMAPS.Circle(options);
}
function createGoogleMarker(options) {
return new GMAPS.Marker(options);
}
function createInfoWindow(content, maxWidth) {
return new GMAPS.InfoWindow({
content: content,
maxWidth: maxWidth
});
}
function addMouseOverListener(marker, actionFunction) {
return GEVENT.addListener(marker, 'mouseover', actionFunction);
}
function addMouseOutListener(marker, actionFunction) {
return GEVENT.addListener(marker, 'mouseout', actionFunction);
}
function addClickListener(marker, actionFunction) {
return GEVENT.addListener(marker, 'click', actionFunction);
}
function removeListeners(handlers) {
$.each(handlers, function(){
GEVENT.removeListener(this);
});
}

View file

@ -0,0 +1,67 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
var map;
var markerManager;
var universityModeMarkerManagers;
var textControl;
var legendControl;
var downloader;
var currentVisMode;
var currentController;
var visModeControllers = {};
function loadMap() {
var gMap = google.maps;
var centerLatLng = new google.maps.LatLng(50, 0);
var mapOptions = {
center: centerLatLng,
zoom: 1,
mapTypeControlOptions: {
mapTypeIds: []
}
};
var mapAreaId = $("#map_area");
map = new gMap.Map(mapAreaId[0], mapOptions);
var mapName = 'Scimap';
createScimapType(map, mapName);
map.setMapTypeId(mapName);
}
function initMapControls() {
//textControl = new TextControl({map: map, divClass: 'time_div'});
//textControl.setText("1991");
}
function initVisModeController() {
var controller = new EntityVisModeController(map);
visModeControllers[controller.visMode] = controller;
switchVisMode(controller.visMode);
}
function initMarkers() {
downloader = new DownloadManager();
loadMarkers(ENTITY_VIS_MODE, "smallSampleData.json", false);
}
function initMap() {
loadMap();
initMapControls();
initVisModeController();
initMarkers();
}
function helper() {
/* override helper function to avoid reload script */
}
/* Using .load instead of .ready due to issue with IE and Google Maps API */
$(window).load(function() {
initMap();
});

View file

@ -0,0 +1,103 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
/**
* The MarkerManager is more like a composite class of Marker. It manages
* markers by grouping the markers by keys.
*/
var MarkerManager = Class.extend({
init: function(colorStrategy, sizeCoder) {
this.keyToMarker = {};
this.colorStrategy = colorStrategy;
this.sizeCoder = sizeCoder;
},
addMarker: function(key, marker) {
this.keyToMarker[key] = marker;
},
getMarker: function(key) {
return this.keyToMarker[key];
},
hasKey: function(key) {
return (this.keyToMarker.hasOwnProperty(key));
},
showMarkers: function() {
$.each(this.keyToMarker, function(i, marker) {
marker.show();
});
},
hideMarkers: function() {
$.each(this.keyToMarker, function(i, marker) {
marker.hide();
});
},
addMarkersToMap: function() {
console.log(this.keyToMarker);
$.each(this.keyToMarker, function(i, marker) {
marker.addToMap();
});
},
removeMarkersFromMap: function() {
$.each(this.keyToMarker, function(i, marker) {
marker.removeFromMap();
});
}
});
/**
* Customized MarkerManager for Science map purpose. It is be an abstract class
*/
ScimapMarkerManager = MarkerManager.extend({
init: function(map, colorStrategy, sizeCoder) {
this._super();
this.colorStrategy = colorStrategy;
this.sizeCoder = sizeCoder;
this.map = map;
this.layer = {};
},
setSizeCoder: function(sizeCoder) {
this.sizeCoder = sizeCoder;
},
createMarker: function(key, density) {
var me = this;
var marker;
if (!me.hasKey(key)) {
var size = me.sizeCoder.getSize(density);
var color = me.colorStrategy.getColor(key);
var layer = me.layer;
var label = layer[key].label;
var latlng = createNoWrapLatLng(layer[key].latitude, layer[key].longitude);
marker = createScinodeMarker(me.map, label, density, size, color, latlng);
me.addMarker(key, marker);
} else {
marker = me.keyToMarker[key];
marker.setValue(marker.getValue() + density);
marker.setSize(me.sizeCoder.getSize(marker.getValue()));
}
return marker;
},
updateMarkerViews: function() {
var me = this;
for (var key in me.keyToMarker) {
var marker = me.keyToMarker[key];
marker.setSize(me.sizeCodingFunc(marker.getValue()));
marker.setColor(me.colorStrategy.getColor(key));
}
}
});
var DisciplineMarkerManager = ScimapMarkerManager.extend({
init: function(map, colorStrategy, sizeCoder) {
this._super(map, colorStrategy, sizeCoder);
this.layer = DISCIPLINES;
},
createMarker: function(subdisciplineKey, density) {
var me = this;
var key = SUBDISCIPLINES[subdisciplineKey].discipline;
return this._super(key, density);
}
});
var SubdisciplineMarkerManager = ScimapMarkerManager.extend({
init: function(map, colorStrategy, sizeCoder) {
this._super(map, colorStrategy, sizeCoder);
this.layer = SUBDISCIPLINES;
}
});

View file

@ -0,0 +1,53 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
var Polygon = Class.extend({
init : function(options) {
this.options = $.extend({}, this.options, options);
if (options.polygon) {
this.polygon = options.polygon;
} else {
this.polygon = createGoogleCirclePolygon(options);
}
this.hide();
this.registerEvents();
},
options : {
map : null,
icon : null,
position : null,
content : null
},
addToMap : function() {
this.polygon.setMap(this.options.map);
this.registerEvents();
},
removeFromMap : function() {
this.polygon.setMap(null);
this.unregisterEvents();
},
show : function() {
this.polygon.setMap(this.options.map);
},
hide : function() {
this.polygon.setMap(null);
},
setIcon : function(icon) {
},
setZIndex: function(zIndex){
this.polygon.zIndex = zIndex;
},
setTitle : function(title) {
this.polygon.title = title;
},
registerEvents : function() {
var handlers = new Array();
var polygon = this.polygon;
handlers.push(addClickListener(polygon, function() {
updateIFrame(this.url);
}));
this.handlers = handlers;
},
unregisterEvents : function() {
removeListeners(this.handlers);
this.handlers = null;
}
});

View file

@ -0,0 +1,76 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
/**
* The scaler is used for scaling based on the predefined minimum value and
* maximum value. You also can control the returned maximum scale and minimum
* scale.
*/
var Scaler = Class.extend({
init: function(options) {
this.options = $.extend({}, this.options, options);
},
options: {
scaleFunc: ReallySimpleAreaScale,
minValue: 0.0,
maxValue: 0.0,
minScale: 0.0,
maxScale: 1.0
},
getScale: function(value) {
var o = this.options;
var scale = o.scaleFunc(value, o.minValue, o.maxValue, o.minScale, o.maxScale);
if (scale > o.maxScale) {
scale = maxScale;
} else if (scale < o.minScale) {
scale = minScale;
}
return scale;
}
});
/* Scaling that ignore minScale and maxScale */
function ReallySimpleAreaScale(value, minValue, maxValue, minScale, maxScale) {
return maxScale * Math.sqrt(value / maxValue);
}
/* Scaling that cares about minScale and maxScale */
function SimpleAreaScale(value, minValue, maxValue, minScale, maxScale) {
if (maxValue != minValue) {
var scale = minScale;
if (value > minValue) {
var valueDiff = maxValue - minValue;
var areaScale = value / valueDiff;
scale = Math.sqrt(areaScale);
}
return scale;
} else {
return maxScale;
}
}
/**
* SizeCoder use scaler to scale its interested size based on the given
* scaler strategy.
*/
var CircleSizeCoder = Class.extend({
init: function(options) {
this.options = $.extend({}, this.options, options);
},
options: {
minRadius: 100000.0,
maxRadius: 2500000.0,//2500000.0,
scaler: new Scaler({})
},
getSize: function(value) {
var o = this.options;
var radius = o.scaler.getScale(value) * o.maxRadius;
if (radius < o.minRadius) {
radius = o.minRadius;
} else if (radius > o.maxRadius) {
radius = o.maxRadius;
}
return radius;
}
});

View file

@ -0,0 +1,59 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
function switchMarkerManager(id) {
markerManager = getMarkerManager(id);
if(isActiveMarkerManager(markerManager)) {
markerManager.addAllToMap();
if(activeMarkerManager) {
activeMarkerManager.removeAllFromMap();
}
/* switch to target marker manager */
activeMarkerManager = markerManager;
}
}
function isActiveVisMode(visMode) {
return (currentVisMode == visMode);
}
function getVisModeController(visMode){
return visModeControllers[visMode];
}
function switchVisMode(visMode) {
if (currentVisMode != visMode) {
currentVisMode = visMode;
if (currentController) {
currentController.cleanView();
}
currentController = getVisModeController(visMode);
currentController.initView();
}
return
}
function loadMarkers(visMode, url, sync) {
// Download data from server and add to markerManager if not gotten already
var controller = getVisModeController(visMode);
if (controller.needLoaded()) {
if (sync) {
downloader.downloadAndWait(url, function(data) {
loadJSONToMarkerManager(data, visMode);
});
} else {
downloader.download(url, function(data) {
loadJSONToMarkerManager(data, visMode);
});
}
} // end if
}
function loadJSONToMarkerManager(data, visMode) {
if (data) {
var controller = getVisModeController(visMode);
controller.loadJsonData(data[0]);
}
}

View file

@ -0,0 +1,89 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
var ENTITY_VIS_MODE = "ENTITY";
var EntityVisModeController = Class.extend({
init: function(map) {
this.keyToMarkerManagers = {};
this.activeManager = null;
this.visMode = ENTITY_VIS_MODE;
this.isUnloaded = true;
this.map = map;
this.initMarkerManagers(map);
},
initMarkerManagers: function(map) {
var managers = this.keyToMarkerManagers;
// Create discipline Marker Manager
managers['discipline'] = new DisciplineMarkerManager(
map,
new DisciplineColorStrategy(),
null
);
// Create subdiscipline Marker Manager
managers['subdiscipline'] = new SubdisciplineMarkerManager(
map,
new SubdisciplineColorStrategy(),
null
);
},
initView: function(){
this.show('subdiscipline');
},
needLoaded: function(){
return this.isUnloaded;
},
loadJsonData: function(data) {
var me = this;
me.uri = data.uri;
me.label = data.label;
me.pubsWithNoJournals = data.pubsWithNoJournals;
me.pubsWithInvalidJournals = data.pubsWithInvalidJournals;
me.pubsMapped = data.pubsMapped;
$.each(this.keyToMarkerManagers, function(key, manager) {
// Need to create the AreaSizeCoding function
manager.setSizeCoder(new CircleSizeCoder({
scaler: new Scaler({ maxValue: me.pubsMapped })
}));
//markerManager.setSiseCodingFunction(new AreaSizeCoding(0, data.pubsMapped));
$.each(data.subdisciplineActivity, function(subdiscipline, density) {
// Create marker and add it to manager
var marker = manager.createMarker(subdiscipline, density);
if (isActiveVisMode(me.visMode) && manager == me.activeManager) {
marker.show();
}
}); // end each subdisciplineActivity
}); // end each markerManagers
this.isUnloaded = false;
},
getMarkerManager: function(key) {
return this.keyToMarkerManagers[key];
},
hasKey: function(key) {
return (this.keyToMarkerManagers.hasOwnProperty(key));
},
show: function(key) {
var manager = this.getMarkerManager(key);
var activeManager = this.activeManager;
if (activeManager != manager) {
this.cleanUp();
manager.addMarkersToMap();
}
this.activeManager = manager;
},
hide: function(key) {
var manager = this.getMarkerManager(key);
if (this.activeManager == manager) {
this.cleanup();
}
},
cleanUp: function() {
if (this.activeManager) {
this.activeManager.removeMarkersFromMap();
}
}
});