Source: FactoryPatterns/PropSymbol.js

/**
 * A class to create proportional symbol maps.
 *
 * @class      PropSymbolCreator (PropSymbolCreator)
 */

function PropSymbolCreator() {
};

PropSymbolCreator.prototype = Object.create(MapCreator.prototype);
PropSymbolCreator.prototype.constructor = PropSymbolCreator;

//
// Creates a proportional symbol ol.source, adds circle features,
// creates a layer and adds it to the map.
// "With 2D proportional symbols like circles and squares (see example below), it is the area of the symbols that encodes the data, not their height or length."https://gis.stackexchange.com/questions/165101/when-is-it-best-to-use-a-proportional-symbol-map-or-a-graduated-symbol-map
//
// @param      {ol.map}   map                      The map
// @param      {ol.source}   vectorSource             The vector source
// @param      {ol.source}   wardsSource              The boundaries source
// @param      {ol.source}       symbolSource             The proportional symbol source
// @param      {number[]}   colorsPerClass           The colors per class
// @param      {string}   featureToDisplay         The feature to display
// @param      {string}   selectedValue            The selected value
// @param      {MapDesign}   MD                    MapDesign reference
// @param      {ol.layer}   symbolLayer              The symbol layer
// @param      {boolean}  isDiscrete               Indicates if discrete
// @param      {string}   standardizationProperty  The standardization property key of each feature
//
PropSymbolCreator.prototype.createMap = function(map, vectorSource, wardsSource, symbolSource, colorsPerClass, featureToDisplay, selectedValue, MD, symbolLayer, isDiscrete, standardizationProperty) {
    if (wardsSource.getFeatures().length <= 0) {
        return;
    }
    if (vectorSource.getFeatures().length <= 0) {

        return;
    }

    // http://thematicmapping.org/playground/javascript/openlayers_propsymbols_geojson.js 
    var symbol = new ol.geom.Geometry('circle', 10, 1312978855);

    //GetDropDownValueOfSelectedClassValue. The dropdown set visible and populated with vectorLayerClasses[]
    //The user has to select the value for this option
    //This is the amount of classes ranges in which the count of the selected feature is displayed. 
    var amountOfColorClasses = colorsPerClass.length; // This is specified by the user or AI
    var pointFeatures = vectorSource.getFeatures();
    var boundriesGeometry = wardsSource.getFeatures();
    // var isDiscrete = false;
    if (isDiscrete) {
         console.log("PPS:Discr");
        // console.log(boundriesGeometry[0].getGeometry().getExtent());
        // console.log("WardFeatureCount= " + boundriesGeometry.length + ", PFCount= " + pointFeatures.length);
        var countOfClassPerWard;
        var countPerWard = [];
        var colorsPerWard = []; //hue per ward that determines color. Range 0-360
        for (var k = 0; k < amountOfColorClasses; k++) {
            colorsPerWard.push(0);
            countPerWard.push(0);
        }

        var maxClassCountOfWard = -1;
        var minClassCountOfWard = Number.POSITIVE_INFINITY;
        for (var i = 0; i < boundriesGeometry.length; i++) {
            countOfClassPerWard = 0;
            for (var j = 0; j < pointFeatures.length; j++) {
                if (ol.extent.intersects(pointFeatures[j].getGeometry().getExtent(), boundriesGeometry[i].getGeometry().getExtent())) {
                    if(pointFeatures[j].get(featureToDisplay) != null)
                    {
                        if ((pointFeatures[j].get(featureToDisplay)).localeCompare($("#attrValue option:selected").html()) == 0) {
                            ++countOfClassPerWard;
                        } else {

                        }
                    }
                }
            }
            if (countOfClassPerWard < minClassCountOfWard) {
                minClassCountOfWard = countOfClassPerWard;
            }
            if (countOfClassPerWard > maxClassCountOfWard) {
                maxClassCountOfWard = countOfClassPerWard;
            }
            countPerWard[i] = countOfClassPerWard;
        }
        if (maxClassCountOfWard > 0) {
            var symbolStyle = new ol.style.Style({
                fill: new ol.style.Fill({
                    color: 'rgba(27, 104, 51, 0.7)'
                }),
                stroke: new ol.style.Stroke({
                    color: 'rgba(27, 104, 51, 1)',
                    width: 1
                }),
                visible: true
            });
            symbolSource = new ol.source.Vector({});
            symbolLayer.layer = new ol.layer.Vector({
                source: symbolSource,
                style: symbolStyle
            });

            var currentColorFraction = 0;
            //Upper limit for each class, where class 0's minimum = minClassCountOfWard;
            var classRangeMax = [];
            var currentHue = 0;

            for (var i = 0; i < amountOfColorClasses; i++) {
                classRangeMax.push(minClassCountOfWard + (currentColorFraction * (maxClassCountOfWard - minClassCountOfWard)));
                currentColorFraction += 1 / amountOfColorClasses;
                colorsPerWard[i] = i / amountOfColorClasses * 360;
                // console.log("Class #" + i + " 's max is " + classRangeMax[i] + " size = " + colorsPerWard[i]);
            }
            for (var i = 0; i < boundriesGeometry.length; i++) {
                var curExtent = boundriesGeometry[i].getGeometry().getExtent();
                var center = ol.extent.getCenter(curExtent);
                var normalisationVariable = ol.extent.getArea(curExtent);
                var newSymbolFeature = new ol.Feature(new ol.geom.Circle(center, ((countPerWard[i] / maxClassCountOfWard /** (maxClassCountOfWard - minClassCountOfWard)) + minClassCountOfWard )*/ * 0.0002 / normalisationVariable))))
                symbolSource.addFeature(newSymbolFeature); // Cite A 
            }
            map.addLayer(symbolLayer.layer);
        } else {
            console.log("PPS:No classes");
        }
        // map.removeLayer(featureLayer);
    } else {
        console.log("PPS:Cont");
        // var countOfClassPerWard;
        // var countPerWard = [];
        // var sumPerWard = [];
        // for (var i = 0; i < boundriesGeometry.length; i++) {
        //     countOfClassPerWard = 0;
        //     sumPerWard.push(0);
        //     for (var j = 0; j < pointFeatures.length; j++) {
        //         if (ol.extent.intersects(pointFeatures[j].getGeometry().getExtent(), boundriesGeometry[i].getGeometry().getExtent())) {
        //             sumPerWard[i] += pointFeatures[j].get(featureToDisplay);
        //             countOfClassPerWard++;
        //         }
        //     }
        //     countPerWard[i] = countOfClassPerWard;
        // }

        var symbolStyle = new ol.style.Style({
            fill: new ol.style.Fill({
                color: 'rgba(27, 104, 51, 0.3)'
            }),
            stroke: new ol.style.Stroke({
                color: 'rgba(27, 104, 51, 1)',
                width: 1
            }),
            visible: true
        });
        symbolSource = new ol.source.Vector({});
        symbolLayer.layer = new ol.layer.Vector({
            source: symbolSource,
            style: symbolStyle
        });

        var targetMin = 0.001;
        var targetMax = 0.1;
        var min = boundriesGeometry[0].get(standardizationProperty);
        var max = boundriesGeometry[0].get(standardizationProperty);
        var current;
        var wardsSourceCount = boundriesGeometry.length;
        for (var i = 0; i < wardsSourceCount; i++) {
            current = boundriesGeometry[i].get(standardizationProperty);
            if (current < min) {
                min = current;
            }
            if (current > max) {
                max = current;
            }
        };
        console.log("data min " +min);
        console.log("data max " +max);

        var boundriesGeometry = wardsSource.getFeatures();
        var wardsSourceCount = boundriesGeometry.length;
        console.log("SDP" + standardizationProperty);
        console.log(boundriesGeometry[0].get(standardizationProperty));
        for (var i = 0; i < wardsSourceCount; i++) {


            // //temp
            // if (i == 1) {
            //     var curExtent = boundriesGeometry[i].getGeometry().getExtent();
            //     var center = ol.extent.getCenter(curExtent);
            //     var newSymbolFeature = new ol.Feature(new ol.geom.Circle(center, 0.1));
            //     symbolSource.addFeature(newSymbolFeature); // Cite A
            // }

            // var radius = boundriesGeometry[i].get(standardizationProperty) * 1000;
            var beforeNormalisation = boundriesGeometry[i].get(standardizationProperty);
            // console.log("BFN" + beforeNormalisation);
            var radius = (((targetMax - targetMin) * (beforeNormalisation - min)) / (max - min)) + targetMin;
            // console.log("Radius " + radius);
            if (radius > 0) {
                var curExtent = boundriesGeometry[i].getGeometry().getExtent();
                var center = ol.extent.getCenter(curExtent);
                var newSymbolFeature = new ol.Feature(new ol.geom.Circle(center, radius));
                symbolSource.addFeature(newSymbolFeature); // Cite A 
            } else {
                console.log("standardised ward value 0, didnt create feature");
            }
        };

        map.addLayer(symbolLayer.layer);

        // var currentColorFraction = 0;
        // //Upper limit for each class, where class 0's minimum = minClassCountOfWard;
        // var classRangeMax = [];
        // var currentHue = 0;
        // for (var i = 0; i < boundriesGeometry.length; i++) {
        //     if (sumPerWard[i] > 0) {
        //         var curExtent = boundriesGeometry[i].getGeometry().getExtent();
        //         var center = ol.extent.getCenter(curExtent);
        //         // var normalisationVariable = ol.extent.getArea(curExtent); 
        //         var newSymbolFeature = new ol.Feature(new ol.geom.Circle(center, sumPerWard[i]));
        //         newSymbolFeature.set()

        //         newSymbolFeature.setStyle(symbolStyle);
        //         ////////////////////////////MAKE VESCTORSTYLE
        //     }
        // }
    }
};

/**
 * A concrete product of a proportional symbol map creator.
 *
 * @class      defaultMapDesign
 */
function PropSymbolConcrete(map) {
    this.map = map;
};

PropSymbolConcrete.prototype = Object.create(MapProduct.prototype);
PropSymbolConcrete.prototype.constructor = PropSymbolConcrete;