diff --git a/js/NatAtlas.js b/js/NatAtlas.js index d02ad3738f5c7cddb79a34011fe8ce1699e30f73..521dde75df3c56b57395fde1d031e12097d917a7 100644 --- a/js/NatAtlas.js +++ b/js/NatAtlas.js @@ -6,19 +6,21 @@ * * @author Barend Köbben - b.j.kobben@utwente.nl * - * @version 0.6.0 [July 2015] + * @version 0.6.0 [August 2015] * - First attempt at new MapChooser * - repaired circles sizes to use Math.PI - * - use object fro dataStats + * - use object for dataStats * - Changed metadata to one file with several languages * - moved common styles to natatlas.css * - cleaned up all: clear distinction globals/locals/attributes * - more error checking in metadata loading + * - added legends (based on d3.legend by Susie Lu: d3-legend.susielu.com) * */ -// metadata as bootstrap, all other data is in there: -var METADATA_URL = "./data/metaData.json"; +// metadata as bootstrap, all other necessary data is in there: +var METADATA_URL; +METADATA_URL = "./data/metaData.json"; var MD; //global MetaDataObject // global constants: @@ -30,26 +32,18 @@ const errorMsg = 0, showMsg = 1, hideMsg = 2, debugMsg = 3; // TODO: Make rescalable (responsive?) const mapDivHeight = 650, mapDivWidth = 550; - // global vars: var numClasses = 5; var minCircleSize = 0; var maxCircleSize = 20; - var mapgroup = -1, mapsubject = -1, mapunit = -1, mapdate = -1; - var DEBUG, DEBUG1; var curLang; var mainMap, mainMapBG, compareMap, compareMapBG; -var mapMenu; var map_dims = {map_scale: 0.0, y_offset: 0.0, x_offset: 0.0}; -var numMapGroups, numMapSubjectsInGroup; -var menuDiv, legendDiv, messageDiv; +var legendDiv, messageDiv; var geo_path; - - -//var waterColor = "#ccddf2"; var tooltip; @@ -87,12 +81,11 @@ function init(language) { theError = "HTTP " + error.status + "--" + error.statusText; } setMessage([ - "BOOTSTRAP MISLUKT: Laden MD mislukt\nURL= " + metadataURL + "\n" + theError, - "BOOTSTRAP FAILED: Error loading MD\nURL= " + metadataURL + "\n" + theError + "BOOTSTRAP MISLUKT: Laden MetaData mislukt\nURL= " + metadataURL + "\n" + theError, + "BOOTSTRAP FAILED: Error loading MetaData\nURL= " + metadataURL + "\n" + theError ], errorMsg); } else { MD = json; //make global - // use RD projection limits to calculate scale and bounds needed for // affine transformation of RD coordinates to screen coordinates var map_minx = 13600; @@ -118,6 +111,7 @@ function init(language) { mainMap = mainMapSVG.append("g") .attr("id", "mainMap") ; + //note compareMap is created, hidden at start (-> CSS) var compareMapSVG = d3.select("#compareMapDiv").append("svg") .attr("id", "compareMapSVG") .attr("width", mapDivWidth) @@ -137,10 +131,7 @@ function init(language) { map_dims.x_offset, map_dims.y_offset)) ; - // use metadata to create chooser menu - var mapMenu = createMenuTree(MD); - - // load background map from basemap data and render it + // load background maps from basemap data and render it // using the attrib baseMapClassAttr as a class name createBackgroundMap(mainMapBG, MD.baseMapDataURL, MD.baseMapClassAttr); createBackgroundMap(compareMapBG, MD.baseMapDataURL, MD.baseMapClassAttr); @@ -151,9 +142,11 @@ function init(language) { tooltip = d3.select("body") .append("div") .attr("class", "tooltip") - .style("opacity", 0); + .style("opacity", 0) + ; tooltip.append("div") - .attr("class", "tooltip-text"); + .attr("class", "tooltip-text") + ; }//init() @@ -241,39 +234,75 @@ function setMessage(messageStrs, messageType) { */ function createMenuTree(MD) { - numMapGroups = MD.mapgroups.length; - numMapSubjectsInGroup = []; + //document.getElementById("makeMapBtn").style.visibility = "hidden"; + + //fold open div: + d3.select("#chooserDiv") + .transition().duration(1000) + .style("width", "500px") + .style("height", "400px") + ; + //clean up open menus: + d3.select("#mGroup").selectAll("input").remove(); + d3.select("#mSubject").selectAll("input").remove(); + d3.select("#mUnit").selectAll("input").remove(); + d3.select("#mDate").selectAll("input").remove(); - for (i = 0; i < numMapGroups; i++) { - numMapSubjectsInGroup[i] = MD.mapgroups[i].mapsubjects.length; + + + var mapGroupsList = d3.select("#mGroup"); + for (i = 0; i < MD.mapgroups.length; i++) { + mapGroupsList.append("input") + .attr("type", "button") + .attr("value", MD.mapgroups[i].groupname[curLang]) + .attr("onclick", "showMapSubjects(" + i + ");") + ; } - for (i = 0; i < numMapGroups; i++) { - mapMenu = d3.select("#chooserDiv") - .append("h2") - .html(MD.mapgroups[i].groupname[curLang] + "<p>") - .append("select") - .attr("class", "menu") - .attr("id", "mapGroupSelect_" + i) - .attr("onchange", "mapsubject = " + - " document.getElementById('mapGroupSelect_' +" + i + ").value") +} + +function showMapSubjects(mapGroup) { + + //clean up open menus: + d3.select("#mSubject").selectAll("input").remove(); + d3.select("#mUnit").selectAll("input").remove(); + d3.select("#mDate").selectAll("input").remove(); + var mapSubjectsList = d3.select("#mSubject"); + for (i = 0; i < MD.mapgroups[mapGroup].mapsubjects.length; i++) { + mapSubjectsList.append("input") + .attr("type", "button") + .attr("value", MD.mapgroups[mapGroup].mapsubjects[i].name[curLang]) + .attr("onclick", "showMapUnits(" + mapGroup + "," + i + ");") ; - mapMenu.append("option") - .html("Choose...") - .attr("value", "-1") + } +} + +function showMapUnits(mapGroup, mapSubject) { + + //clean up open menus: + d3.select("#mUnit").selectAll("input").remove(); + d3.select("#mDate").selectAll("input").remove(); + var mapUnitsList = d3.select("#mUnit"); + for (i = 0; i < MD.mapgroups[mapGroup].mapsubjects[mapSubject].mapunits.length; i++) { + mapUnitsList.append("input") + .attr("type", "button") + .attr("value", MD.mapgroups[mapGroup].mapsubjects[mapSubject].mapunits[i].name[curLang]) + .attr("onclick", "showMapDates(" + mapGroup + "," + mapSubject + "," + i + ");") ; - for (j = 0; j < numMapSubjectsInGroup[i]; j++) { - mapMenu.append("option") - .attr("value", j) - .html(MD.mapgroups[i].mapsubjects[j].name[curLang]) - ; - } } +} - var MakeMakeButton = d3.select("#chooserDiv") - .append("input").attr("type", "submit").attr("value", "Make Map") - .attr("onclick", "chooseMap(mapgroup, mapsubject, mapunit, mapdate);"); +function showMapDates(mapGroup, mapSubject, mapUnit) { - return mapMenu; + //clean up open menus: + d3.select("#mDate").selectAll("input").remove(); + var mapDatesList = d3.select("#mDate"); + for (i = 0; i < MD.mapgroups[mapGroup].mapsubjects[mapSubject].mapunits[mapUnit].mapdates.length; i++) { + mapDatesList.append("input") + .attr("type", "button") + .attr("value", MD.mapgroups[mapGroup].mapsubjects[mapSubject].mapunits[mapUnit].mapdates[i].date) + .attr("onclick", "chooseMap(" + mapGroup + "," + mapSubject + "," + mapUnit + "," + i + ");") + ; + } } /** @@ -320,6 +349,15 @@ function createBackgroundMap(mapLayer, URL, theClassAttr) { * */ function chooseMap(mapgroup, mapsubject, mapunit, mapdate) { + //fold down chooserDiv: + d3.select("#chooserDiv") + .transition().duration(250) + .style("width", "175px") + .style("height", "20px") + ; + document.getElementById("makeMapBtn").style.visibility = "visible"; + + var geoData = undefined; // empty data layer var attribData = undefined; // empty attrib layer @@ -344,8 +382,12 @@ function chooseMap(mapgroup, mapsubject, mapunit, mapdate) { var FK = MD.mapgroups[mapgroup].mapsubjects[mapsubject].mapunits[mapunit].mapdates[mapdate].FK; // geo_data loader: - var geoMD = MD.geo_sources[MD.mapgroups[mapgroup].mapsubjects[mapsubject].mapunits[mapunit].mapdates[mapdate].geo_data]; - var geoURL = geoMD.serviceURL; + try { + var geoMD = MD.geo_sources[MD.mapgroups[mapgroup].mapsubjects[mapsubject].mapunits[mapunit].mapdates[mapdate].geo_data]; + var geoURL = geoMD.serviceURL; + } catch (e) { + console.log(e); + } setMessage(["", "Loading geodata; URL=" + geoURL], debugMsg); d3.json(geoURL, function (error, json) { if (error != undefined) { @@ -368,39 +410,83 @@ function chooseMap(mapgroup, mapsubject, mapunit, mapdate) { "Invalid format [serviceOutputFormat = " + geoMD.serviceOutputFormat + "]"], errorMsg); } - // attrib_data loader: - var attribMD = MD.attrib_sources[MD.mapgroups[mapgroup].mapsubjects[mapsubject].mapunits[mapunit].mapdates[mapdate].attrib_data]; - var attribURL = attribMD.serviceURL; + try { + var attribMD = MD.attrib_sources[MD.mapgroups[mapgroup].mapsubjects[mapsubject].mapunits[mapunit].mapdates[mapdate].attrib_data]; + var attribURL = attribMD.serviceURL; + } catch (e) { + console.log(e); + } setMessage(["", "Loading attribute data; URL=" + attribURL], debugMsg); - d3.csv(attribURL, function (error, csv) { - if (error != undefined) { - if (error.status == undefined) { // it's not XMLHTTPrequest error} - theError = error.name + ": " + error.message; - } else { - theError = "HTTP " + error.status + "--" + error.statusText; + if (attribMD.serviceOutputFormat == "geojson") { + + d3.json(attribURL, function (error, json) { + if (error != undefined) { + if (error.status == undefined) { // it's not XMLHTTPrequest error} + theError = error.name + ": " + error.message; + } else { + theError = "HTTP " + error.status + "--" + error.statusText; + } + setMessage(["LADEN ATTRIBUUTDATA MISLUKT!\nURL= " + attribURL + ";\nError: " + theError, + "ERROR LOADING ATTRIBUTE DATA!\nURL= " + attribURL + ";\nError: " + theError], errorMsg); + return; } - setMessage(["LADEN ATTRIBUUTDATA MISLUKT!\nURL= " + attribURL + ";\nError: " + theError, - "ERROR LOADING ATTRIBUTE DATA!\nURL= " + attribURL + ";\nError: " + theError], errorMsg); - return; - } - //create a map using FK as key: - attribData = d3.map(csv, function (d) { - var FKval = eval("d." + FK); - if (FKval == undefined) { - setMessage(["Geen geldige FK. Check metadata!\nFK=" + FK + "; FKval=" + FKval, - "No valid FK. Check metadata!\n(FK=" + FK + "; FKval=" + FKval], errorMsg); + //create a map using FK as key: + attribData = d3.map(); + json.features.forEach(function (d,i) { + var FKval = eval("d.properties." + FK); + var valuesObj = d.properties; + if (FKval == undefined || valuesObj == undefined) { + setMessage(["Geen geldige FK. Check metadata!\nFK=" + FK + "; FKval=" + FKval, + "No valid FK. Check metadata!\n(FK=" + FK + "; FKval=" + FKval], errorMsg); + } + attribData.set(FKval,valuesObj); + }); + + setMessage(["Kaartdata geladen.", "Map data loaded."], hideMsg); + createMap(geoData, mainMap); + symboliseMap(geoData, attribData, FK, mainMap, mapgroup, mapsubject, mapunit, mapdate); + setMessage(["Kaart gemaakt.", "Created map."], hideMsg); + }); // geojson attrib_data loader + + } else if (attribMD.serviceOutputFormat == "csv") { + + d3.csv(attribURL, function (error, csv) { + if (error != undefined) { + if (error.status == undefined) { // it's not XMLHTTPrequest error} + theError = error.name + ": " + error.message; + } else { + theError = "HTTP " + error.status + "--" + error.statusText; + } + setMessage(["LADEN ATTRIBUUTDATA MISLUKT!\nURL= " + attribURL + ";\nError: " + theError, + "ERROR LOADING ATTRIBUTE DATA!\nURL= " + attribURL + ";\nError: " + theError], errorMsg); + return; } - return FKval; - }); - setMessage(["Kaartdata geladen.", "Map data loaded."], hideMsg); - createMap(geoData, mainMap); - symboliseMap(geoData, attribData, FK, mainMap, mapgroup, mapsubject, mapunit, mapdate); - setMessage(["Kaart gemaakt.", "Created map."], hideMsg); + //create a map using FK as key: + attribData = d3.map(csv, function (d) { + var FKval = eval("d." + FK); + if (FKval == undefined) { + setMessage(["Geen geldige FK. Check metadata!\nFK=" + FK + "; FKval=" + FKval, + "No valid FK. Check metadata!\n(FK=" + FK + "; FKval=" + FKval], errorMsg); + } + return FKval; + }); + + setMessage(["Kaartdata geladen.", "Map data loaded."], hideMsg); + createMap(geoData, mainMap); + symboliseMap(geoData, attribData, FK, mainMap, mapgroup, mapsubject, mapunit, mapdate); + setMessage(["Kaart gemaakt.", "Created map."], hideMsg); + }); // CSV attrib_data loader + + } else { + setMessage(["Ongeldig formaat [serviceOutputFormat = " + attribMD.serviceOutputFormat + "]", + "Invalid format [serviceOutputFormat = " + attribMD.serviceOutputFormat + "]"], errorMsg); + } + + - }); //attrib_data loader }); //geo_data loader } //if-else @@ -439,7 +525,7 @@ function createMap(geoData, mapLayer) { .attr("cy", function (d) { return y = Math.round(geo_path.centroid(d)[1]); }) // transform the supplied json geo path centroid Y to svg "cy" - .attr("class", "defaultCircles") // add defualt style (from css) + .attr("class", "defaultCircles") // add default style (from css) .attr("r", 0) // add radius , start with r = 0 .on("mousemove", function () { toolTipMove(d3.event) @@ -483,11 +569,12 @@ function symboliseMap(geoData, attribData, FK, mapLayer, mapgroup, mapsubject, m var mapFK = MD.mapgroups[mapgroup].mapsubjects[mapsubject].mapunits[mapunit].mapdates[mapdate].FK; var tooltipLabel = MD.mapgroups[mapgroup].mapsubjects[mapsubject].mapunits[mapunit].mapdates[mapdate].label; var mapUnit = MD.mapgroups[mapgroup].mapsubjects[mapsubject].data_unit[curLang]; + var mapClassification = MD.mapgroups[mapgroup].mapsubjects[mapsubject].classification; var dataStats; // *** PROPORTIONAL POINT MAPS **** if (mapType == "point_size") { - dataStats = makeStats(attribData, mapAttrib, numClasses); + dataStats = makeStats(attribData, mapAttrib, mapType, mapClassification); // remove classified polygon fills mapLayer.selectAll("path") // select path nodes .on("mouseenter", function (d) { @@ -502,6 +589,7 @@ function symboliseMap(geoData, attribData, FK, mapLayer, mapgroup, mapsubject, m ; // change proportional circles sizes: mapLayer.selectAll("circle") // select again all the current circle nodes + .style("fill", mapClassification.colours) .on("mouseenter", function (d) { toolTipShow(infoTextFromData(d, attribData, tooltipLabel, mapAttrib, mapFK, mapUnit)); }) @@ -516,7 +604,7 @@ function symboliseMap(geoData, attribData, FK, mapLayer, mapgroup, mapsubject, m // *** CHOROPLETH MAPS **** } else if (mapType == "area_value") { // choropleth map: - dataStats = makeStats(attribData, mapAttrib, numClasses); + dataStats = makeStats(attribData, mapAttrib, mapType, mapClassification); // shrink circles : mapLayer.selectAll("circle") // select again all the current circle nodes .transition().duration(1000) @@ -566,7 +654,7 @@ function symboliseMap(geoData, attribData, FK, mapLayer, mapgroup, mapsubject, m // *** CHOROCHROMATIC MAPS **** } else if (mapType == "area_colour") { // simple label map: - dataStats = makeStats(attribData, mapAttrib, -1); + dataStats = makeStats(attribData, mapAttrib, mapType, mapClassification); // shrink circles : mapLayer.selectAll("circle") // select again all the current circle nodes .transition().duration(1000) @@ -593,17 +681,18 @@ function symboliseMap(geoData, attribData, FK, mapLayer, mapgroup, mapsubject, m } setMessage(["", "Created map symbolisation."], debugMsg); - makeLegend(mapgroup, mapsubject, mapunit, mapdate, mapType, dataStats); + makeLegend(mapgroup, mapsubject, mapunit, mapdate, mapType, mapClassification, dataStats); } function getAttribValue(d, attribData, mapAttrib, mapFK) { var FKval = undefined; var attribValue = undefined; - FKval = eval("d.properties." + mapFK); - attribValue = eval("attribData.get(FKval)." + mapAttrib); - if (FKval == undefined || attribValue == undefined) { - setMessage(["Ongeldige waarde(n)! \nFK=" + FKval + "; attribuut=" + mapAttrib + "; waarde=" + attribValue, - "Invalid value(s)! \n(FK=" + FKval + "; attribute=" + mapAttrib + "; value=" + attribValue], errorMsg); + try { + FKval = eval("d.properties." + mapFK); + attribValue = eval("attribData.get(FKval)." + mapAttrib); + } catch (e) { + setMessage(["Fout in data!\nFK=" + FKval + "; attribuut=" + mapAttrib + "; waarde=" + attribValue, + "Error retrieving data!\n(FK=" + FKval + "; attribute=" + mapAttrib + "; value=" + attribValue], errorMsg); } return attribValue; } @@ -612,7 +701,7 @@ function getAttribValue(d, attribData, mapAttrib, mapFK) { * update legendDiv according to mapgroup/map chosen in menu * */ -function makeLegend(mapgroup, mapsubject, mapunit, mapdate, mapType, dataStats) { +function makeLegend(mapgroup, mapsubject, mapunit, mapdate, mapType, mapClassification, dataStats) { var geoSourceStr = ["Geodata", "Geodata"]; var dataSourceStr = ["Attribuut data", "Attribute data"]; @@ -620,16 +709,17 @@ function makeLegend(mapgroup, mapsubject, mapunit, mapdate, mapType, dataStats) var mapObj = MD.mapgroups[mapgroup].mapsubjects[mapsubject].mapunits[mapunit].mapdates[mapdate]; var legendHeader = "<h3>" + MD.mapgroups[mapgroup].groupname[curLang] + "</h3>"; - legendHeader += "<h1>" + MD.mapgroups[mapgroup].mapsubjects[mapsubject].name[curLang] + "</h1>"; - legendHeader += "<h4>" + MD.mapgroups[mapgroup].mapsubjects[mapsubject].data_unit[curLang]; - legendHeader += " per " + MD.mapgroups[mapgroup].mapsubjects[mapsubject].mapunits[mapunit].name[curLang]; - legendHeader += " (" + mapObj.date + ")</h4>"; + legendHeader += "<h1>" + MD.mapgroups[mapgroup].mapsubjects[mapsubject].name[curLang] + "</h1><h4>"; + if (MD.mapgroups[mapgroup].mapsubjects[mapsubject].mapunits[mapunit].name[curLang] != "") { + legendHeader += "per " + MD.mapgroups[mapgroup].mapsubjects[mapsubject].mapunits[mapunit].name[curLang] + " "; + } + legendHeader += "(" + mapObj.date + ")</h4>"; + if (MD.mapgroups[mapgroup].mapsubjects[mapsubject].data_unit[curLang] != "") { + legendHeader += MD.mapgroups[mapgroup].mapsubjects[mapsubject].data_unit[curLang] + ":"; + } legendDiv.html(legendHeader); - - DEBUG = dataStats; - var legendSVG = legendDiv.append("svg") .attr("id", "legendSVG") .attr("width", "100%") @@ -644,12 +734,12 @@ function makeLegend(mapgroup, mapsubject, mapunit, mapdate, mapType, dataStats) .attr("class", "mySizeLegend") .attr("transform", "translate(20,20)") ; - var linearSize = d3.scale.linear().domain([0,dataStats.dMax]).range([0, maxCircleSize]); + var linearSize = d3.scale.linear().domain([0, dataStats.dMax]).range([0, maxCircleSize]); //var linearSize = d3.scale.linear().domain([0,100]).range([10, 20]); var mySizeLegend = d3.legend.size() .scale(linearSize) - .labelFormat(d3.format(".0f")) + .labelFormat(d3.format(mapClassification.format)) .shape('circle') .shapePadding(2) .labelOffset(1) @@ -659,6 +749,8 @@ function makeLegend(mapgroup, mapsubject, mapunit, mapdate, mapType, dataStats) legendSVG.select(".mySizeLegend") .call(mySizeLegend) ; + legendSVG.selectAll("circle").style("fill", mapClassification.colours); + legendSVG.style("height", mySizeLegend.legendHeight()); // *** CHOROPLETH MAPS **** @@ -670,7 +762,7 @@ function makeLegend(mapgroup, mapsubject, mapunit, mapdate, mapType, dataStats) .attr("transform", "translate(0,0)") ; var myColorLegend = d3.legend.color() - .labelFormat(d3.format(".0f")) + .labelFormat(d3.format(mapClassification.format)) .labelDelimiter("–") .shapeWidth(20) .useClass(false) @@ -748,44 +840,135 @@ function infoTextFromData(d, attribData, labelAttrib, mapAttrib, mapFK, mapUnit) } -function makeStats(attribData, attrib, numClasses) { +function makeStats(attribData, attrib, mapType, mapClassification) { var myStats = { dValues: undefined, dMin: undefined, dMax: undefined, dCircleRatio: undefined, - dJenksClasses: undefined, dClass2Value: undefined, dClass2Colour: undefined + dClasses: undefined, dClass2Value: undefined, dClass2Colour: undefined }; var numFeatures = attribData.size(); myStats.dValues = new Array(numFeatures); var i = 0; + var errorStr = ""; attribData.forEach(function (k, v) { - myStats.dValues[i] = +eval("v." + attrib); //+ to force numerical + if (mapType == "point_size" || mapType == "area_value") { + myStats.dValues[i] = +eval("v." + attrib); //+ to force numerical + if (myStats.dValues[i] == undefined || isNaN(myStats.dValues[i])) { + errorStr = "Maptype=" + mapType + "; data=" + myStats.dValues[i]; + } + } else { //area_label or area_colour + myStats.dValues[i] = eval("v." + attrib); + if (myStats.dValues[i] == undefined) { + errorStr = "Maptype=" + mapType + "; data=" + myStats.dValues[i]; + } + } i++; }); + if (errorStr != "") { + setMessage(["ONGELDIGE DATA VOOR DIT MAPTYPE!\n" + errorStr, + "INVALID DATA FOR THIS MAPTYPE!\n" + errorStr], errorMsg); + console.log(myStats.dValues); + } - if (numClasses == -1) { //non numerical data - // an ordinal scale for (chorochromatic) nominal maps - myStats.dClass2Colour = d3.scale.ordinal() // make a classes array using d3 ordinal - .range(colorbrewer.Spectral[11]) // assign to 11 classes Spectral CB range - } else { + var clStr = "type=" + mapClassification.type + "; numclasses=" + mapClassification.numclasses + "; classes=" + + mapClassification.classes + "; colours=" + mapClassification.colours + "; format=" + mapClassification.format; + console.log(clStr); + + // *** PROPORTIONAL POINT MAPS **** + if (mapType == "point_size") { myStats.dMin = d3.min(myStats.dValues); //TODO: less crappy solution for NoData vals: if (myStats.dMin <= -99999997) { // is NoData myStats.dMin = 0 - }// + } myStats.dMax = d3.max(myStats.dValues); - //now determine key measures: // a ratio between values and circles radius for (proportional) ratio maps: myStats.dCircleRatio = maxCircleSize / (Math.sqrt(myStats.dMax) / Math.PI ); - //use Jenks.js to calculate Jenks Natural breaks for x classes - myStats.dJenksClasses = jenks(myStats.dValues, numClasses); + + // *** CHOROPLETH MAPS **** + } else if (mapType == "area_value") { // choropleth map: + myStats.dMin = d3.min(myStats.dValues); + //TODO: less crappy solution for NoData vals: + if (myStats.dMin <= -99999997) { // is NoData + myStats.dMin = 0 + } + myStats.dMax = d3.max(myStats.dValues); + if (mapClassification.numclasses == undefined) { + mapClassification.numclasses = 5; + } + if (mapClassification.numclasses < 3 || mapClassification.numclasses > 11) { + InvalidClassMessage(clStr + "\nInvalid numclasses (<3 or >11)."); + } + if (mapClassification.type == "jenks") { //use jenks.js to calculate Jenks Natural breaks + myStats.dClasses = jenks(myStats.dValues, mapClassification.numclasses); + } else if (mapClassification.type == "manual") { // use manual + if (mapClassification.classes == undefined) { + InvalidClassMessage(clStr + "\nClasses array needed for manual classification."); + } + if (mapClassification.classes[0] == "dMin") { + mapClassification.classes[0] = myStats.dMin; + } + if (mapClassification.classes[mapClassification.classes.length - 1] == "dMax") { + mapClassification.classes[mapClassification.classes.length - 1] = myStats.dMax; + } + //check manual classes + if (mapClassification.classes[0] > myStats.dMin) { + InvalidClassMessage(clStr + "\nData min < lowest class."); + } else if (mapClassification.classes[mapClassification.classes.length - 1] < myStats.dMax) { + InvalidClassMessage(clStr + "\nData max > highest class."); + } else if (mapClassification.classes.length - 1 != mapClassification.numclasses) { + InvalidClassMessage(clStr + "\nClasses array length does not match number of classes."); + } else { // all correct + myStats.dClasses = mapClassification.classes; + } + + } else { + InvalidClassMessage(clStr + "\nInvalid type."); + } // a classed scale for (choropleth) ordered or relative ratio maps: + try { + var CBrange = eval("colorbrewer." + mapClassification.colours + "[" + mapClassification.numclasses + "]"); + } catch (e) { + InvalidClassMessage(clStr + "\n'" + mapClassification.colours + "' is not a valid ColorBrewer name."); + } myStats.dClass2Value = d3.scale.quantile() - .domain(myStats.dJenksClasses) // use Jenks classes (see above) - .range(colorbrewer.Greens[numClasses]) // assign to numClasses classes in Green CB range + .domain(myStats.dClasses) // use jenks or manual classes (see above) + .range(CBrange) ; + + // *** LABEL MAPS **** + } else if (mapType == "area_label") { // simple label map: + + // *** CHOROCHROMATIC MAPS **** + } else if (mapType == "area_colour") { // simple label map: + + // an ordinal scale for (chorochromatic) nominal maps + if (mapClassification.numclasses == undefined) { + mapClassification.numclasses = 25; //25 is max for scale MaxColours + } + if (mapClassification.numclasses < 3 || mapClassification.numclasses > 25) { + InvalidClassMessage(clStr + "\nInvalid numclasses (<3 or >24)."); + } + try { + var CBrange = eval("colorbrewer." + mapClassification.colours + "[" + mapClassification.numclasses + "]"); + } catch (e) { + InvalidClassMessage(clStr + "\n'" + mapClassification.colours + "' not valid ColorBrewer name, or no. of classes not available."); + } + myStats.dClass2Colour = d3.scale.ordinal() // make a classes array using d3 ordinal + .range(CBrange) + + } else { + setMessage( + ["Onbekend Map Type [" + mapType + "]", "Unknown Map Type [" + mapType + "]"], errorMsg); } + setMessage(["", "Calculated map statistics."], debugMsg); //if (debugOn) console.log(myStats); return myStats; +} + +function InvalidClassMessage(Str) { + setMessage(["ONGELDIGE CLASSIFICATIE!\n" + Str, + "INVALID CLASSIFICATION!\n" + Str], errorMsg); } \ No newline at end of file