diff --git a/nginx/covid_website/assets/css/style.css b/nginx/covid_website/assets/css/style.css index 9883eafd13ab5f3801dd427fc1ba6c47225830ee..7e236fd777a7ff6e3976310e7cf396a7c8322adf 100644 --- a/nginx/covid_website/assets/css/style.css +++ b/nginx/covid_website/assets/css/style.css @@ -1011,3 +1011,12 @@ header { max-height: 25px !important; max-width: 65px !important; } + +.studyLegend { + cursor: pointer; +} +.studyLegend i { + color: black; + font-style: normal; + padding-left: 4px; +} diff --git a/nginx/covid_website/assets/js/cumulativePlot.js b/nginx/covid_website/assets/js/cumulativePlot.js index ccaa9a9e621443d9deb8997910979a60d32ef258..6e4689328affa41e49eb0736c72e0417e5218386 100644 --- a/nginx/covid_website/assets/js/cumulativePlot.js +++ b/nginx/covid_website/assets/js/cumulativePlot.js @@ -156,10 +156,10 @@ function makeCumPlot(geojsonData, cumPlotDiv) { } - console.log(cumFreqATMP); - console.log(cumFreqVaccines); - console.log(cumFreqTradChin); - console.log(cumFreqImpHelpers); + // console.log(cumFreqATMP); + // console.log(cumFreqVaccines); + // console.log(cumFreqTradChin); + // console.log(cumFreqImpHelpers); //---------------------------------------- @@ -320,4 +320,4 @@ function makeCumPlot(geojsonData, cumPlotDiv) { var maxDate = regDates[regDates.length - 1] return maxDate -}; \ No newline at end of file +}; diff --git a/nginx/covid_website/assets/js/map.js b/nginx/covid_website/assets/js/map.js deleted file mode 100644 index b6e006369cdb23a8bac7b52201c072c480970372..0000000000000000000000000000000000000000 --- a/nginx/covid_website/assets/js/map.js +++ /dev/null @@ -1,342 +0,0 @@ -function loadAllMaps() { - - mapDivs = ['map-study-categories'] - - for (i = 0; i < mapDivs.length; i++) { - initMap(mapDivs[i]) - } - -} - -function initMap(mapDiv) { - // get current date and format to the right format - var currentDate = new Date().toISOString().split('T')[0]; - var 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 Carto, under CC BY 3.0. Data by OpenStreetMap, 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); - - // add legend - if (mapDiv == "map-study-types") { - addLegend_studyType(map); - } else if (mapDiv == "map-study-categories") { - addLegend_studyCategory(map); - } - - // add scale bar - var scale = L.control.scale( - { - 'imperial': false, - 'updateWhenIdle': true - } - ); - scale.addTo(map); - - // add HeiGIT logo - var logo = L.control({position: 'bottomleft'}); - logo.onAdd = function(map){ - var div = L.DomUtil.create('div', 'logoContainer'); - div.innerHTML= ""; - return div; - } - logo.addTo(map); - -} - -function addLegend_studyType(map) { - legend = L.control({position: 'bottomleft'}); - legend.onAdd = function (map) { - var div = L.DomUtil.create('div', 'info legend') - div.innerHTML += 'Observational Study
' - div.innerHTML += 'Interventional Study
' - div.innerHTML += 'Other
' - return div; - }; - legend.addTo(map); -} - -function addLegend_studyCategory(map) { - legend = L.control({position: 'bottomleft'}); - legend.onAdd = function (map) { - var div = L.DomUtil.create('div', 'info legend') - div.innerHTML += 'Diagnosis
' - div.innerHTML += 'Disease understanding
' - div.innerHTML += 'Therapeutic stategies
' - div.innerHTML += 'Drugs
' - div.innerHTML += 'Advanced therapy medicinal products
' - div.innerHTML += 'Vaccines
' - div.innerHTML += 'Traditional Chinese medicine
' - div.innerHTML += 'Impact on helpers
' - //div.innerHTML += 'Epidemiological Study
' - div.innerHTML += 'Other
' - return div; - }; - legend.addTo(map); -} - -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."), - /*error: function (xhr) { - alert(xhr.statusText) - }*/ - }) - // 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.geoJson(layer) - timeDimension_layer.bindPopup(function (timeDimension_layer) { - // popup with a link to the project page with detailed information - popup = '

'+layer.feature.properties.name+'

' - 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: ${properties.name}
- Description: ${properties.description}
- Study type: ${properties.study_type}
- Classification: ${properties.classification}
- Registration Date: ${properties.time}
- Date Enrollment: ${properties.dateEnrollment}
- Contact: ${properties.contactEmail}`; - 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 = '

'+element.properties.name+'

' - tr.appendChild(td) - - td = document.createElement('td') - td.innerHTML = '

'+element.properties.description+'

' - tr.appendChild(td) - - td = document.createElement('td') - td.innerHTML = '

'+element.properties.study_type+'

' - tr.appendChild(td) - - td = document.createElement('td') - td.innerHTML = '

'+element.properties.classification+'

' - tr.appendChild(td) - - td = document.createElement('td') - td.innerHTML = '

'+element.properties.time+'

' - tr.appendChild(td) - - td = document.createElement('td') - td.innerHTML = '

'+element.properties.dateEnrollment+'

' - tr.appendChild(td) - - td = document.createElement('td') - td.innerHTML = '

'+element.properties.primaryOutcome+'

' - tr.appendChild(td) - }) - - $('#'+tableDivID + ' tfoot th').each( function () { - var title = $(this).text(); - $(this).html( '' ); - } ); - - 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(); - } - } ); - }); - -}; - - diff --git a/nginx/covid_website/assets/js/time-dimension-by-taxonomy.js b/nginx/covid_website/assets/js/time-dimension-by-taxonomy.js new file mode 100644 index 0000000000000000000000000000000000000000..7d365a6f747cdf7f0c7c3b7d5783a4ffcb5f8f41 --- /dev/null +++ b/nginx/covid_website/assets/js/time-dimension-by-taxonomy.js @@ -0,0 +1,60 @@ +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); + }, + + /** + * Overwrites the _update method to implement the + * clustering feature to the map items vizualization + */ + _update: function () { + if (!this._map) + return; + if (!this._loaded) { + return; + } + + var maxTime = this._timeDimension.getCurrentTime(), minTime = 0; + + if (this._duration) { + var date = new Date(maxTime); + L.TimeDimension.Util.subtractTimeDuration(date, this._duration, true); + minTime = date.getTime(); + } + + // new coordinates: + var layer = L.geoJson(null, this._baseLayer.options); + var layers = this._baseLayer.getLayers(); + + for (var i = 0, l = layers.length; i < l; i++) { + var feature = this._getFeatureBetweenDates(layers[i].feature, minTime, maxTime); + if (feature && this.disabledTaxonomies.indexOf(feature.properties.classification) === -1) { + layer.addData(feature); + + if (this._addlastPoint && feature.geometry.type == "LineString") { + if (feature.geometry.coordinates.length > 0) { + var properties = feature.properties; + properties.last = true; + let coordinates = feature.geometry.coordinates[feature.geometry.coordinates.length - 1] + layer.addData({ type: 'Feature', properties: properties, geometry: {type: 'Point', coordinates: coordinates }}); + } + } + } + } + + if (this._currentLayer) { + this._map.removeLayer(this._currentLayer); + } + if (layer.getLayers().length) { + layer.addTo(this._map); + this._currentLayer = layer; + } + }, +}); + +L.timeDimension.layer.geoJsonByType = function (geoJsonLayer, options) { + return new L.TimeDimension.Layer.GeoJsonByType(geoJsonLayer, options); +}; diff --git a/nginx/covid_website/assets/js/trials-map.js b/nginx/covid_website/assets/js/trials-map.js new file mode 100644 index 0000000000000000000000000000000000000000..7bb8348fe8b03424f652364fe2ac29e1db444c50 --- /dev/null +++ b/nginx/covid_website/assets/js/trials-map.js @@ -0,0 +1,446 @@ +/** + * 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
Carto, under CC BY 3.0. Data by OpenStreetMap, 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 = `` + 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)

' + let fragment = new DocumentFragment() + + for (let type in this.studyTypeColors) { + // Create span element for each category + let spanEl = document.createElement('span') + spanEl.innerText = type.charAt(0).toUpperCase() + type.slice(1) + spanEl.className = 'studyLegend' + spanEl.title = 'Click to hide/show' + + // Create i element to be appended to each span + let iEl = document.createElement('i') + iEl.style.background = this.studyTypeColors[type] + + // Append i element to the span + spanEl.appendChild(iEl); + + // Append span and a line break + fragment.appendChild(spanEl); + fragment.appendChild(document.createElement('br')) + } + + // Append the types listing to the legend + typesFragment.appendChild(fragment) + return typesFragment + } + + /** + * Add study cateogires legend overlayer + * with toggle cagetegory visibility function + */ + addLegendStudyCategory = () => { + // If it was already loaded before, remove before loading again + if (this.legendByCategory) { + this.map.removeControl(this.legendByCategory) + } + this.legendByCategory = L.control({ position: 'bottomleft' }) + let that = this + this.legendByCategory.onAdd = function () { + return that.buildCategoriesLegendFragment() + } + this.legendByCategory.addTo(this.map) + } + + /** + * Build categories legend html fragment + * @returns {DocumentFragment} fragment + */ + buildCategoriesLegendFragment = () => { + var categoriesFragment = L.DomUtil.create('div', 'info legend') + categoriesFragment.innerHTML += 'Study categories (click to hide/show)

' + let fragment = new DocumentFragment(); + + for (let category in this.studyCategoryColors) { + // Create span element for each category + let spanEl = document.createElement('span') + spanEl.innerText = category.charAt(0).toUpperCase() + category.slice(1) + spanEl.className = 'studyLegend' + spanEl.title = 'Click to hide/show' + spanEl.onclick = () => { this.toggleCategoryVisibility(category) } + + // Create i element to be appended to each span + let iEl = document.createElement('i') + iEl.style.background = this.studyCategoryColors[category] + iEl.innerText = this.timeDimension_layer && this.timeDimension_layer.disabledTaxonomies.indexOf(category) > -1 ? '' : 'X' + + // Append i element to the span + spanEl.appendChild(iEl); + + // Append span and a line break + fragment.appendChild(spanEl); + fragment.appendChild(document.createElement('br')) + } + + // Append the categories listing/toggle to the legend + categoriesFragment.appendChild(fragment) + return categoriesFragment + } + + /** + * Toggle the visibility of a given category + * when run, the function will update the studies loaded + * on the map and also refresh the map overlays + * in order to update the given category overlay status (visible or not) + * @param {String} category + */ + toggleCategoryVisibility = (category) => { + let catIndex = this.timeDimension_layer.disabledTaxonomies.indexOf(category) + if (catIndex > -1) { + this.timeDimension_layer.disabledTaxonomies.splice(catIndex, 1) + } else { + this.timeDimension_layer.disabledTaxonomies.push(category) + } + this.timeDimension_layer._update(category) + this.setMapOverlayers() + } + + /** + * Buind and return the style object for a given study + * type circle marker + * @param {*} feature + * @return {*} style + */ + getStyleStudyType = (feature) => { + let type = feature.properties.study_type + + let style = { fillColor: 'grey', color: 'black', radius: 7, fillOpacity: 0.5, weight: 1, opacity: 1 } + if (this.studyTypeColors[type]) { + style.fillColor = this.studyTypeColors[type] + } + return style + } + + /** + * Buind and return the style object for a given study + * category circle marker + * @param {*} feature + * @return {*} style + */ + getStyleStudyCategory = (feature) => { + let category = feature.properties.classification + + let style = { fillColor: '#666666', color: 'grey', radius: 7, fillOpacity: 0.5, weight: 1, opacity: .7, } + if (this.studyCategoryColors[category]) { + style.fillColor = this.studyCategoryColors[category] + } + + return style + } + + /** + * Add the geojson layer after loading the geojson data from projectCentroidsUrl + */ + addGeojsonLayer = () => { + let that = this + var geojsonData = $.ajax({ url: this.projectCentroidsUrl, dataType: "json" }) + + // Specify that this code should run once the county data request is complete + $.when(geojsonData).done(function () { + let layer = that.buidGeojsonLayer(geojsonData.responseJSON) + + that.timeDimension_layer = L.timeDimension.layer.geoJsonByType(layer) + // popup with a link to the project page with detailed information + that.timeDimension_layer.bindPopup(function () { popup = '

' + layer.feature.properties.name + '

'; return popup; }) + that.timeDimension_layer.addTo(that.map) + + // fit to layer extent + that.map.fitBounds(layer.getBounds()) + + // add plot only for first map + // makePlot a global function + makePlot(geojsonData.responseJSON, that.plotEl) + + that.populateDataTable(geojsonData.responseJSON, that.trialsEl) + + // add timestamp of date to text + document.getElementById(that.lastUpdateEl).innerHTML = makeCumPlot(geojsonData.responseJSON, that.cumplotEl) + document.getElementById(that.trialsCountEl).innerHTML = geojsonData.responseJSON.features.length + }) + } + + /** + * Build geojson layer based on jsonData + * @param {*} jsonData + * @returns {*} layer + */ + buidGeojsonLayer = (jsonData) => { + let layer + let that = this + // create geojson layer + if (this.loadByTaxonomy=== "categories") { + // classification layer + layer = L.geoJSON(jsonData, { + pointToLayer: function (feature, latlng) { + let marker = L.circleMarker(latlng, {}) + marker.on("click", function (data) { that.circleMarkerClick(data, that.map) }) + return marker + }, + style: this.getStyleStudyCategory + }) + } else if (this.loadByTaxonomy== "types") { + layer = L.geoJSON(jsonData, { + pointToLayer: function (feature, latlng) { + let marker = L.circleMarker(latlng, {}); + marker.on("click", function (data) { that.circleMarkerClick(data, that.map) }) + return marker + }, + style: this.getStyleStudyType + }) + } + return layer + } + + /** + * Handle the study marker click + * and show the popup for the given study + * @param event + */ + circleMarkerClick = (event) => { + 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: ${properties.name}
+ Description: ${properties.description}
+ Study type: ${properties.study_type}
+ Classification: ${properties.classification}
+ Registration Date: ${properties.time}
+ Date Enrollment: ${properties.dateEnrollment}
+ Contact: ${properties.contactEmail}`; + popup.setLatLng(center); + popup.setContent(popupContent) + this.map.openPopup(popup) + } + + /** + * Populate the datatable using the geojsonData into the tableEl + * and set table scroll and search functions + * @param {*} geojsonData + * @param {String} tableEl + */ + populateDataTable = (geojsonData, tableEl) => { + var tableRef = document.getElementById(tableEl).getElementsByTagName('tbody')[0] + + geojsonData.features.forEach(function (element) { + let tr = tableRef.insertRow() + + let td = document.createElement('td') + td.innerHTML = '

' + element.properties.name + '

' + tr.appendChild(td) + + td = document.createElement('td') + td.innerHTML = '

' + element.properties.description + '

' + tr.appendChild(td) + + td = document.createElement('td') + td.innerHTML = '

' + element.properties.study_type + '

' + tr.appendChild(td) + + td = document.createElement('td') + td.innerHTML = '

' + element.properties.classification + '

' + tr.appendChild(td) + + td = document.createElement('td') + td.innerHTML = '

' + element.properties.time + '

' + tr.appendChild(td) + + td = document.createElement('td') + td.innerHTML = '

' + element.properties.dateEnrollment + '

' + tr.appendChild(td) + + td = document.createElement('td') + td.innerHTML = '

' + element.properties.primaryOutcome + '

' + tr.appendChild(td) + }) + + $('#' + tableEl + ' tfoot th').each(function () { + var title = $(this).text(); + $(this).html('') + }) + + var table = $('#' + tableEl).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() + } + }) + }) + } +} diff --git a/nginx/covid_website/clinical_trials.html b/nginx/covid_website/clinical_trials.html index 1455ebf1e13a115dd8685517d24d64dba752c4eb..89cefdc6bb0910b56f6764d55725e69a85ec6b56 100644 --- a/nginx/covid_website/clinical_trials.html +++ b/nginx/covid_website/clinical_trials.html @@ -1,224 +1,222 @@ - - - - - Research on Covid-19 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- + +
-
-
-

Mapping COVID-19 Research

-

The "Map of Hope" provides a geographical overview of planned, ongoing and completed clinical trials.

-
-
-
- -
-
-
-

Clinical Trials Map

- -

The information on clinical trials is based on data from the WHO Clinical Trials Search Portal for COVID-19 related clinical trials. The Clinical Trials Search Portal provides access to a central database containing the trial registration data sets provided by many international registries

-

The WHO portal gets updated every Friday by six important registries and every 4 weeks by additional registries. We aim at updating our maps without too much delay.

-

The data shown reflects the WHO portal updated at 03 April 2020 and contains clinical trials.

+
+
+

Mapping COVID-19 Research

+

The "Map of Hope" provides a geographical overview of planned, ongoing and + completed clinical trials.

+
-
- -
-
-
- -
- - -
-
- -
- -
- -

The map shows the clinical trial locations classified by the study category. The time slider allows browsing by the temporal dimension. Only clinical trials that were registered before a specified date are shown. The location of the clinical trial is only accurate to the level of the city and does not identify the institution where the trial took place. Click on an individual study to display more detailed information about the study.

-

-
-
- -
-
-

Cumulative plots by study category. It is possible to switch categories on and off by clicking on the respective legend item. If you hover across the plot area you will see a context menu that allows you to zoom, show data, toogle spike lines and to export the figure.

-

+
+
+
+

Clinical Trials Map

+

The information on clinical trials is based on data from the WHO Clinical Trials Search Portal for COVID-19 related + clinical trials. The Clinical Trials Search Portal provides access to a central database containing the + trial registration data sets provided by many international registries

+

The WHO portal gets updated every Friday by six important registries and every 4 weeks by additional + registries. We aim at updating our maps without too much delay.

+

The data shown reflects the WHO portal updated at 03 April 2020 and + contains clinical trials.

+
+
-
-
- - -
-
-
-
-

Data

-

The following table presents some of the details on the records displayed on the map as offered by the WHO Clinical Trials Search Portal. Horizontal scroll bars allow to access the full content of the table. Follow the links for full detail.

-

Filter options

-

The Search field performs a full text query on all fields. Further search fields at the bottom of the table allow you to filter the table for each column individually.

+
+
+
+
+ +
+
+ +
+
+

The map shows the clinical trial locations classified by the study category. The time slider allows + browsing by the temporal dimension. Only clinical trials that were registered before a specified date are + shown. The location of the clinical trial is only accurate to the level of the city and does not identify + the institution where the trial took place. Click on an individual study to display more detailed + information about the study.

+

+
+
+ +
+
+

Cumulative plots by study category. It is possible to switch categories on and off by clicking on the + respective legend item. If you hover across the plot area you will see a context menu that allows you to + zoom, show data, toogle spike lines and to export the figure.

+

+
-
- - - - - - - - - - - - - - - - - - - - - - - - - - -
TrialIDDescriptionStudy TypeClassificationRegistration dateEnrollment datePrimary outcome
TrialIDDescriptionStudy TypeClassificationRegistration dateEnrollment datePrimary outcome
+
+
+
+
+
+

Data

+

The following table presents some of the details on the records displayed on the map as offered by the WHO Clinical Trials Search Portal. Horizontal scroll bars + allow to access the full content of the table. Follow the links for full detail.

+

Filter options

+

The Search field performs a full text query on all fields. Further search fields at the bottom of + the table allow you to filter the table for each column individually.

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
TrialIDDescriptionStudy TypeClassificationRegistration dateEnrollment datePrimary outcome
TrialIDDescriptionStudy TypeClassificationRegistration dateEnrollment datePrimary outcome
+
-
-
- -
-
-
-

Disclaimer: Trials posted on this search portal are neither endorsed by WHO nor by HeiGIT, but are provided as a service to our users. In no event shall the World Health Organization or HeiGIT be liable for any damages arising from the use of the information linked to in this section. None of the information obtained through use of the services provided should in any way be used in clinical care without consulting a physician or licensed health professional. Neither WHO nor HeiGIT is responsible for the accuracy, completeness and/or use made of the content displayed for any trial record.

-
-
- -
- -
-
-
- -
- - - - - - - - - - - - + + + + + + + + +