Commit 924212c9 authored by Amon's avatar Amon
Browse files

Implemented toggle category visible and refactored js

parent c368b682
......@@ -1018,5 +1018,5 @@ header {
.studyLegend i {
color: black;
font-style: normal;
padding-left: 3px;
padding-left: 4px;
}
function loadAllMaps() {
mapDivs = ['map-study-categories']
for (i = 0; i < mapDivs.length; i++) {
initMap(mapDivs[i])
}
}
function initMap(mapDiv) {
window.mapDiv = mapDiv
// get current date and format to the right format
var currentDate = new Date().toISOString().split('T')[0];
var map = window.map = L.map(mapDiv, {
zoom: 2,
minZoom: 1,
maxZoom: 12,
center: [0.0, 0.0],
timeDimension: true,
timeDimensionOptions: {
timeInterval: "2020-01-20/".concat(currentDate),
period: "P1D"
},
timeDimensionControl: false,
timeDimensionControlOptions: {
position: "topright",
}
});
L.tileLayer('https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png', {
attribution: 'Map tiles by <a href="https://carto.com/">Carto</a>, under <a href="https://creativecommons.org/licenses/by/3.0/">CC BY 3.0.</a> Data by <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>, under ODbL.',
subdomains: ['a', 'b', 'c']
}).addTo(map);
// load geojson file
projectCentroidsUrl = 'assets/data/covid-19-trials.geojson';
setTimeout(function () {
map.invalidateSize()
}, 400);
var plotDiv = mapDiv.replace('map', 'plot');
var plotCumDiv = mapDiv.replace('map', 'cumplot');
addGeojsonLayer(map, projectCentroidsUrl, mapDiv, plotDiv, plotCumDiv);
// add custom date format to the time dimension control
L.Control.TimeDimensionCustom = L.Control.TimeDimension.extend({
_getDisplayDateFormat: function (date) {
return date.toISOString().split('T')[0];
}
});
var timeDimensionControl = new L.Control.TimeDimensionCustom({
position: "topright"
});
map.addControl(timeDimensionControl);
setMapOverlayers();
}
function setMapOverlayers() {
// add legend
if (mapDiv == "map-study-types") {
addLegend_studyType(map);
} else if (mapDiv == "map-study-categories") {
addLegend_studyCategory(map);
}
if (window.scale) {
map.removeControl(scale);
}
// add scale bar
window.scale = L.control.scale({
'imperial': false,
'updateWhenIdle': true
});
scale.addTo(map);
if (window.logo) {
map.removeControl(logo);
}
// add HeiGIT logo
window.logo = L.control({
position: 'bottomleft'
});
logo.onAdd = function (map) {
var div = L.DomUtil.create('div', 'logoContainer');
div.innerHTML = "<img src='assets/img/logos/heigit_logo.png'/>";
return div;
}
logo.addTo(window.map);
}
function addLegend_studyType(map) {
if (window.legendByType) {
map.removeControl(legendByType);
}
legendByType = L.control({
position: 'bottomleft'
});
legendByType.onAdd = function (map) {
var div = L.DomUtil.create('div', 'info legend')
div.innerHTML += '<i style="background:orange"></i>Observational Study<br>'
div.innerHTML += '<i style="background:blue"></i>Interventional Study<br>'
div.innerHTML += '<i style="background:grey"></i>Other<br>'
return div;
};
legendByType.addTo(map);
}
function addLegend_studyCategory(map) {
if (window.legendByCategory) {
map.removeControl(legendByCategory);
}
legendByCategory = L.control({
position: 'bottomleft'
});
legendByCategory.onAdd = function (map) {
var div = L.DomUtil.create('div', 'info legend')
div.innerHTML += 'Study types (click to hide/show)<br><br>'
div.innerHTML += `<span class="studyLegend" title="Click to hide/show" onclick="toggleType('diagnosis')" ><i style="background:#1B9E77">${typeVisible('diagnosis')}</i>Diagnosis</span><br>`
div.innerHTML += `<span class="studyLegend" title="Click to hide/show" onclick="toggleType('understanding')" ><i style="background:#BF5B17">${typeVisible('understanding')}</i>Disease understanding</span><br>`
div.innerHTML += `<span class="studyLegend" title="Click to hide/show" onclick="toggleType('management')"><i style="background:#386CB0">${typeVisible('management')}</i>Therapeutic stategies</span><br>`
div.innerHTML += `<span class="studyLegend" title="Click to hide/show" onclick="toggleType('drugs')"><i style="background:#7FC97F">${typeVisible('drugs')}</i>Drugs</span><br>`
div.innerHTML += `<span class="studyLegend" title="Click to hide/show" onclick="toggleType('ATMP')"><i style="background:#FDC086">${typeVisible('ATMP')}</i>Advanced therapy medicinal products</span><br>`
div.innerHTML += `<span class="studyLegend" title="Click to hide/show" onclick="toggleType('vaccine')"><i style="background:#BEAED4">${typeVisible('vaccine')}</i>Vaccines</span><br>`
div.innerHTML += `<span class="studyLegend" title="Click to hide/show" onclick="toggleType('traditional chinese medicine')"><i style="background:#F0027F">${typeVisible('traditional chinese medicine')}</i>Traditional Chinese medicine</span><br>`
div.innerHTML += `<span class="studyLegend" title="Click to hide/show" onclick="toggleType('impact on the helpers')"><i style="background:#FFFF99">${typeVisible('impact on the helpers')}</i>Impact on helpers</span><br>`
div.innerHTML += `<span class="studyLegend" title="Click to hide/show" onclick="toggleType('other')"><i style="background:#666666">${typeVisible('other')}</i>Other</span><br>`
return div;
};
legendByCategory.addTo(map);
}
function typeVisible (type) {
if (window.timeDimension_layer !== undefined) {
let visible = timeDimension_layer.disabledTypes.indexOf(type) > -1;
return visible ? '' : 'X';
}
return 'X';
}
function toggleType (type) {
timeDimension_layer.disabledTypes = timeDimension_layer.disabledTypes || [];
const typeIndex = timeDimension_layer.disabledTypes.indexOf(type);
if (typeIndex > -1) {
timeDimension_layer.disabledTypes.splice(typeIndex, 1);
} else {
timeDimension_layer.disabledTypes.push(type);
}
timeDimension_layer._update(type);
setMapOverlayers();
}
function set_style_studyType(feature) {
if (feature.properties.study_type == "Observational study") {
return {
fillColor: 'orange',
color: 'black',
radius: 7,
fillOpacity: 0.5
}
} else if (feature.properties.study_type == "Interventional study") {
return {
fillColor: 'blue',
radius: 7
}
} else {
return {
fillColor: 'grey',
radius: 7
}
}
}
function set_style_studyCategory(feature) {
varStrokeOpacity = .7
if (feature.properties.classification == "drugs") {
return {
fillColor: '#7FC97F',
color: 'grey',
radius: 7,
opacity: varStrokeOpacity,
fillOpacity: 0.5
}
} else if (feature.properties.classification == "vaccine") {
return {
fillColor: '#BEAED4',
fillOpacity: 0.5,
opacity: varStrokeOpacity,
radius: 7
}
} else if (feature.properties.classification == "ATMP") {
return {
fillColor: '#FDC086',
color: 'grey',
fillOpacity: 0.5,
opacity: varStrokeOpacity,
radius: 7
}
} else if (feature.properties.classification == "impact on the helpers") {
return {
fillColor: '#FFFF99',
color: 'grey',
fillOpacity: 0.5,
opacity: varStrokeOpacity,
radius: 7
}
} else if (feature.properties.classification == "management") {
return {
fillColor: '#386CB0',
fillOpacity: 0.5,
opacity: varStrokeOpacity,
radius: 7
}
} else if (feature.properties.classification == "traditional chinese medicine") {
return {
fillColor: '#F0027F',
fillOpacity: 0.5,
opacity: varStrokeOpacity,
radius: 7
}
} else if (feature.properties.classification == "diagnosis") {
return {
fillColor: '#1B9E77',
fillOpacity: 0.5,
opacity: varStrokeOpacity,
radius: 7
}
} else if (feature.properties.classification == "understanding") {
return {
fillColor: '#BF5B17',
fillOpacity: 0.5,
opacity: varStrokeOpacity,
radius: 7
}
} else {
return {
fillColor: '#666666',
fillOpacity: 0.5,
opacity: varStrokeOpacity,
radius: 7
}
}
}
function addGeojsonLayer(map, url, mapDiv, plotDiv, cumPlotDiv) {
var geojsonData = $.ajax({
url: url,
dataType: "json",
success: console.log("clinical trials data successfully loaded.")
})
// Specify that this code should run once the county data request is complete
$.when(geojsonData).done(function () {
// define default point style
var geojsonMarker = {
radius: 6,
fillColor: "grey",
color: "white",
weight: 1,
opacity: 1,
fillOpacity: 0.8
};
// create geojson layer
if (mapDiv == "map-study-categories") {
// classification layer
layer = L.geoJSON(geojsonData.responseJSON, {
pointToLayer: function (feature, latlng) {
let marker = L.circleMarker(latlng, geojsonMarker);
marker.on("click", function (data) {
circleMarkerClick(data, map)
});
return marker;
},
style: set_style_studyCategory
})
} else if (mapDiv == "map-study-types") {
layer = L.geoJSON(geojsonData.responseJSON, {
pointToLayer: function (feature, latlng) {
let marker = L.circleMarker(latlng, geojsonMarker);
marker.on("click", function (data) {
circleMarkerClick(data, map)
});
return marker;
},
style: set_style_studyType
})
}
timeDimension_layer = L.timeDimension.layer.geoJsonByType(layer);
timeDimension_layer.disabledTypes = []
timeDimension_layer.bindPopup(function (timeDimension_layer) {
// popup with a link to the project page with detailed information
popup = '<p>' + layer.feature.properties.name + '</p>'
return popup;
});
timeDimension_layer.addTo(map)
// fit to layer extent
map.fitBounds(layer.getBounds());
// add plot only for first map
makePlot(geojsonData.responseJSON, plotDiv)
maxDate = makeCumPlot(geojsonData.responseJSON, cumPlotDiv)
// console.log(maxDate)
// populate table
var tableDivID = 'clinicalTrialsTable'
populateDataTable(geojsonData.responseJSON, tableDivID)
// add timestamp of date to text
document.getElementById("last_update").innerHTML = maxDate
document.getElementById("trials_count").innerHTML = geojsonData.responseJSON.features.length
})
}
function circleMarkerClick(event, map) {
let properties = event.target.feature.properties
let geometry = event.target.feature.geometry
var popup = new L.Popup();
var center = {
lat: geometry.coordinates[1],
lng: geometry.coordinates[0]
}
var popupContent = `
Name: <b><a target="_blank" href=${properties.weburl}>${properties.name}</a></b></br>
Description: <b>${properties.description}</b></br>
Study type: <b>${properties.study_type}</b></br>
Classification: <b>${properties.classification}</b></br>
Registration Date: <b>${properties.time}</b></br>
Date Enrollment: <b>${properties.dateEnrollment}</b></br>
Contact: <b>${properties.contactEmail}</b>`;
popup.setLatLng(center);
popup.setContent(popupContent);
map.openPopup(popup);
}
function populateDataTable(geojsonData, tableDivID) {
var tableRef = document.getElementById(tableDivID).getElementsByTagName('tbody')[0];
geojsonData.features.forEach(function (element) {
tr = tableRef.insertRow();
td = document.createElement('td')
td.innerHTML = '<p><a target="_blank" href="' + element.properties.weburl + '">' + element.properties.name + '</></p>'
tr.appendChild(td)
td = document.createElement('td')
td.innerHTML = '<p>' + element.properties.description + '</p>'
tr.appendChild(td)
td = document.createElement('td')
td.innerHTML = '<p>' + element.properties.study_type + '</p>'
tr.appendChild(td)
td = document.createElement('td')
td.innerHTML = '<p>' + element.properties.classification + '</p>'
tr.appendChild(td)
td = document.createElement('td')
td.innerHTML = '<p>' + element.properties.time + '</p>'
tr.appendChild(td)
td = document.createElement('td')
td.innerHTML = '<p>' + element.properties.dateEnrollment + '</p>'
tr.appendChild(td)
td = document.createElement('td')
td.innerHTML = '<p>' + element.properties.primaryOutcome + '</p>'
tr.appendChild(td)
})
$('#' + tableDivID + ' tfoot th').each(function () {
var title = $(this).text();
$(this).html('<input type="text" placeholder="Search ' + title + '" />');
});
var table = $('#' + tableDivID).DataTable({
"scrollX": true,
scroller: true
});
// Put top scroller
$('.dataTables_scrollHead').css({
'overflow-x': 'scroll'
}).on('scroll', function (e) {
var scrollBody = $(this).parent().find('.dataTables_scrollBody').get(0);
scrollBody.scrollLeft = this.scrollLeft;
$(scrollBody).trigger('scroll');
});
$('.dataTables_length').addClass('bs-select');
// Apply the search
table.columns().every(function () {
var that = this;
$('input', this.footer()).on('keyup change clear', function () {
console.log("event")
if (that.search() !== this.value) {
that
.search(this.value)
.draw();
}
});
});
};
......@@ -2,6 +2,7 @@ L.TimeDimension.Layer.GeoJsonByType = L.TimeDimension.Layer.GeoJson.extend({
initialize: function (geoJsonLayer, options) {
this.geoJsonLayer = geoJsonLayer
this.disabledTaxonomies = []
L.TimeDimension.Layer.GeoJson.prototype.initialize.call(this, geoJsonLayer, options);
},
......@@ -30,7 +31,7 @@ L.TimeDimension.Layer.GeoJsonByType = L.TimeDimension.Layer.GeoJson.extend({
for (var i = 0, l = layers.length; i < l; i++) {
var feature = this._getFeatureBetweenDates(layers[i].feature, minTime, maxTime);
if (feature && this.disabledTypes.indexOf(feature.properties.classification) === -1) {
if (feature && this.disabledTaxonomies.indexOf(feature.properties.classification) === -1) {
layer.addData(feature);
if (this._addlastPoint && feature.geometry.type == "LineString") {
......
/**
* TrialsMap class
* Creates a leaft let map instance, makePlot and populate table
*/
class TrialsMap {
/**
* Constructor of TrialsMap that set the class properties,
* set the taxonomies colors and init that leaflet map
* @param {*} mapEl
* @param {*} trialsEl
* @param {*} options
*/
constructor(mapEl, trialsEl, options= {}) {
this.trialsEl = trialsEl
this.mapEl = mapEl
this.loadByTaxonomy = options.loadByTaxonomy || 'categories' // can be category or type
this.lastUpdateEl = options.lastUpdateEl || 'last_update'
this.trialsCountEl = options.trialsCountEl || 'trials_count'
this.plotEl = options.plotEl || this.mapEl.replace('map', 'plot')
this.cumplotEl = options.cumplotEl || this.mapEl.replace('map', 'cumplot')
this.projectCentroidsUrl = 'assets/data/covid-19-trials.geojson'
this.logoSrc = 'assets/img/logos/heigit_logo.png'
this.tileServiceUrl = 'https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png'
this.mapAttribution = 'Map tiles by <a href="https://carto.com/">Carto</a>, under <a href="https://creativecommons.org/licenses/by/3.0/">CC BY 3.0.</a> Data by <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>, under ODbL.'
this.studyCategoryColors = []
this.studyTypeColors = []
// Set study taxonomies (type and categories) colors
this.setTaxonomiesColors()
this.initMap()
}
/**
* Init map component using leaflet and timedimension
* and set the map overlays
*/
initMap = () => {
let mapOptions = this.buildMapOptions()
this.map = L.map(this.mapEl, mapOptions)
L.tileLayer(this.tileServiceUrl, { attribution: this.mapAttribution, subdomains: ['a', 'b', 'c'] }).addTo(this.map)
let that = this
// load geojson file
setTimeout(function () { that.map.invalidateSize() }, 400)
this.addGeojsonLayer()
// add custom date format to the time dimension control
L.Control.TimeDimensionCustom = L.Control.TimeDimension.extend({
_getDisplayDateFormat: function (date) {
return date.toISOString().split('T')[0]
}
});
var timeDimensionControl = new L.Control.TimeDimensionCustom({ position: "topright" })
this.map.addControl(timeDimensionControl)
this.setMapOverlayers()
}
/**
* Build map options object
* @returns {*} options
*/
buildMapOptions = () => {
var currentDate = new Date().toISOString().split('T')[0]
let mapOptions = {
zoom: 2,
minZoom: 1,
maxZoom: 12,
center: [0.0, 0.0],
timeDimension: true,
timeDimensionOptions: {
timeInterval: "2020-01-20/".concat(currentDate),
period: "P1D" },
timeDimensionControl: false,
timeDimensionControlOptions: { position: "topright"}
}
return mapOptions
}
/**
* Set the type and categories study colors
* by creating the studyCategoryColors and
* studyTypeColors props
*/
setTaxonomiesColors = () => {
this.studyCategoryColors = {
drugs : '#7FC97F',
vaccine: '#BEAED4',
ATMP: '#FDC086',
'impact on the helpers': '#FFFF99',
management: '#386CB0',
'traditional chinese medicine': '#F0027F',
diagnosis: '#1B9E77',
understanding: '#BF5B17',
other: '#666666'
}
this.studyTypeColors = {
'Observational study': 'orange',
'Interventional study': 'blue',
}
}
/**
* Set map layers (legends, scale and logo)
* if the layers already exist, remove them
* and add again, refreshing the data
*/
setMapOverlayers = () => {
// add legend
if (this.loadByTaxonomy=== "types") {
this.addLegendStudyType();
} else if (this.loadByTaxonomy== "categories") {
this.addLegendStudyCategory();
}
if (this.scale) {
this.map.removeControl(this.scale)
}
// add scale bar
this.scale = L.control.scale({'imperial': false, 'updateWhenIdle': true });
this.scale.addTo(this.map)
if (this.logo) {
this.map.removeControl(this.logo)
}
// add HeiGIT logo
this.logo = L.control({ position: 'bottomleft' })
let that = this
this.logo.onAdd = function () {
var logoContainer = L.DomUtil.create('div', 'logoContainer')
logoContainer.innerHTML = `<img src='${that.logoSrc}'/>`
return logoContainer
}
this.logo.addTo(this.map)
}
/**
* Add the study type legend overlayer
*/
addLegendStudyType = () => {
// If it was already loaded before, remove before loading again
if (this.legendByType) {
this.map.removeControl(legendByType)
}
this.legendByType = L.control({ position: 'bottomleft' })
let that = this
this.legendByType.onAdd = function () {
return that.buildStudyTypesFragment()
};
this.legendByType.addTo(this.map);
}
/**
* Build types legend html fragment
* @returns {DocumentFragment} fragment
*/
buildStudyTypesFragment = () => {
var typesFragment = L.DomUtil.create('div', 'info legend')
typesFragment.innerHTML += 'Study types (click to hide/show)<br><br>'