//PROCESS: Using arrays of objects to create a map

<New code>

//PROCESS: loop through each element of an array of objects 
var loopThroughArrayOfObjects = function ( exampleArr ) {
	for ( i in exampleArr ) {			// loop through each object of the array
	
		// do something with exampleArr[ i ].oneProperty
		// do something with exampleArr[ i ].anotherProperty
		// do something with exampleArr[ i ].yetAnotherProperty
	}
}

Advanced example: Creating a map from an array of objects with x and y information

Creating programs that make maps is a very cool, but also complicated.

Often programmers will use libraries and map servers to generate their code for them, such as the software created by ESRI Canada, which, luckily for us, is based in Bedford, Nova Scotia.

A collection of software to make maps is called a Geographic Information System, or GIS. It would be a understatement to say that there are good careers in this field.

Creating your own maps

Let's say we want to create a map from a whole bunch of x and y point data, like latitude and longitude coordinates.

Map making is tricky because of scaling issues:

Once you have these tools, we can create a map with the following procedure:

  1. Declare and initialize the map data as an array of objects having x (longitude) and y (latitude) properties.
  2. Read the screen dimensions.
  3. Discover the minimums and maximums of your .longitude and .latitude data.
  4. Loop through each element of the array.
    1. Each element of the array is an object, and it has object properties that you can access.
    2. Use the .longitude and .latitude properties combined the data minimums and maximums and screen dimensions to calculate a left and top position for a <div>.
    3. Change the class or style of that <div> in order to reflect the other data of the objects, in this case, the .meterDuration property.
  5. Output the collection of <div>s.


...is created with the following code:

Don't be intimidated by the length of the parkingMeterArr array of objects. This program works the same whether there are 3 objects in the array or 30000.


	<style>
            .duration30class {
                width:              1px; 
                height:             1px; 
                border-radius:      0.5px;
                background-color:   purple;
            }
            .duration60class {
                width:              2px; 
                height:             2px; 
                border-radius:      1px;
                background-color:   red;
            }
            .duration90class {
                width:              3px; 
                height:             3px; 
                border-radius:      1.5px;
                background-color:   orange;
            }
            .duration120class {
                width:              4px; 
                height:             4px; 
                border-radius:      2px;
                background-color:   yellow;
            }
            .duration180class {
                width:              5px; 
                height:             5px; 
                border-radius:      2.5px;
                background-color:   blue;
            }
            .duration300class {
                width:              7px; 
                height:             7px; 
                border-radius:      3.5px;
                background-color:   green;
            }
        </style>
	<button id="buttonId">
		Click to generate a map of parking meters in the Halifax Regional Municipality
	</button>
	<script>
            //INIT: declare the array of objects of parking meter data
            //      data taken from http://catalogue.hrm.opendata.arcgis.com/
            var initParkingMeterArr = function () {
                console.log( "in initParkingMeterArr..." );
                
                var parkingMeterArr = [
                    { longitude: -63.5852, latitude: 44.6411, meterDuration: 120 },
                    { longitude: -63.5854, latitude: 44.6412, meterDuration: 120 },
					// and many more objects
                    { longitude: -63.5851, latitude: 44.64102, meterDuration: 120 }
                ];
                
                return parkingMeterArr;
            }
            
            //OUTPUT: display the map inside the document body
            var outputMap = function ( mapCode ) {
                console.log('in outputMap. mapCode=' + mapCode );
                
                document.body.style.backgroundColor = "black";
                document.body.innerHTML = mapCode;
            }

            
            //PROCESS:  Calculate the minimum and maximum values of each property 
            //          in the array of objects.
            //          This assumes that each object is identical in structure and numeric
            //          This returns the property names with either "Max" or "Max" on the end,
            //          such as minMaxObj.longitudeMin, or minMaxObj.latitudeMax.
            var getMinsAndMaxs = function ( objArr ) {
                console.log( 'in getMinsAndMaxs...' );
                
                //create an empty object to hold the minimums and maximums
                var minMaxObj = {};
                
                //set up the initial information for comparison using objArr[ 0 ] {
                minMaxObj.longitudeMin  = objArr[ 0 ].longitude;
                minMaxObj.longitudeMax  = objArr[ 0 ].longitude;
                minMaxObj.latitudeMin   = objArr[ 0 ].latitude;
                minMaxObj.latitudeMax   = objArr[ 0 ].latitude; 
                
                //LOOP: through each object in this array of objects to find the min and max
                for ( i in objArr ) {
                    // store the minimum of this property in property + "Min", 
                    // for example: minMaxObj.longitudeMin
                    minMaxObj.longitudeMin 
                        = Math.min( minMaxObj.longitudeMin, objArr[ i ].longitude );
                    minMaxObj.latitudeMin 
                        = Math.min( minMaxObj.latitudeMin, objArr[ i ].latitude );  
                        
                    // store the maximum of this property in property + "Max",
                    // for example: minMaxObj.longitudeMax
                    minMaxObj.longitudeMax 
                        = Math.max( minMaxObj.longitudeMax, objArr[ i ].longitude );
                    minMaxObj.latitudeMax
                        = Math.max( minMaxObj.latitudeMax, objArr[ i ].latitude );
                }
                
                console.log( 'minMaxObj = ', minMaxObj );
                
                return minMaxObj;
            }
            
            //PROCESS:  
            //  Calculate the positions given x and y coords, min/max, and screen info
            //  Arguments are as an object: {   
            //      xMin:, xMax:, elementX:, yMin:, yMax:, 
            //      elementY:, screenWidth:, screenHeight:
            //  }
            //  Returns css style code: "top: 367px; left: 490px;"
            var getPositionCode = function ( posObj ) {
                console.log( 'in getPositionCode. posObj = ', posObj );
                
                // calculate the data range for both the top and left measures
                var topRange    = posObj.yMax - posObj.yMin;
                var leftRange   = posObj.xMax - posObj.xMin;
                
                // calculate a range & screen dimension so the plotting is as large as possible
                var range, screenDimension;
                var topRangePerPixel = topRange / posObj.screenHeight;
                var leftRangePerPixel = leftRange / posObj.screenWidth;
                
                //IF: the range is taller than it is wide,
                if ( topRangePerPixel > leftRangePerPixel ) {
                    range               = topRange; 
                    screenDimension     = posObj.screenHeight;
                    
                //ELSE: the range is wider than it is tall
                } else {
                    range               = leftRange;
                    screenDimension     = posObj.screenWidth;
                }
                
                // calculate the ratio of the coodinates across the screen
                var topDelta    = posObj.yMax - posObj.elementY;
                var leftDelta   = posObj.xMax - posObj.elementX; 
                var topRatio    = ( ( topDelta ) / range );
                var leftRatio   = 1 - ( ( leftDelta ) / range );

                // calculate the final position on the screen
                var topPosition = Math.round( topRatio * screenDimension );
                var leftPosition = Math.round( leftRatio * screenDimension );
                
                //create the style code to set the top and left position
                var positionCode 
                    = "top: "   + topPosition   + "px; " 
                    + "left: "  + leftPosition  + "px;";
                
                return positionCode 
            }
            
            //INPUT: get the screen height and width (as a number of pixels), and return the info as an object
            var getScreenInformation = function () {
                console.log( "in getScreenInformation..." );
                
                return {
                    width:      window.innerWidth,
                    height:     window.innerHeight
                };
            }
            
            //PROCESS:  create a map of parking meters based on the array of objects
            //          called parkingMeterArr
            var createMapCode = function ( parkingMeterArr ) {
                console.log( 'in createMapCode. parkingMeterArr=', parkingMeterArr );
                
                // get the screen properties
                var screenObj = getScreenInformation();
                
                // get the minimum and maximum values of all the parts of the array of objects
                var dataBoundariesObj = getMinsAndMaxs( parkingMeterArr );
                                
                // create a variable to hold the map's code 
                var mapHTML = "";
                
                //LOOP:     through each element of speedRecordArr 
                //          and create a table row for object contained inside
                for ( i in parkingMeterArr ) {
                    //get the position style code 
                    var positionCode = getPositionCode( 
                        {
                            elementX:           parkingMeterArr[ i ].longitude,
                            elementY:           parkingMeterArr[ i ].latitude,
                            
                            xMin:               dataBoundariesObj.longitudeMin,
                            xMax:               dataBoundariesObj.longitudeMax,
                            yMin:               dataBoundariesObj.latitudeMin,
                            yMax:               dataBoundariesObj.latitudeMax,
                            screenWidth:        screenObj.width,
                            screenHeight:       screenObj.height
                        }
                    );

                    //create the title to display when the div is moused over
                    var title = "latitude: "
                        + parkingMeterArr[ i ].latitude
                        + ", longitude: "
                        + parkingMeterArr[ i ].longitude
                        + ", duration: "
                        + parkingMeterArr[ i ].meterDuration
                        + " min";
                    
                    //construct the div code for this parking meter
                    //example:  
                    //<div class="duration120class" 
                    //  style="position: absolute; top: 630px; left: 256px;" 
                    //  title="latitude: 44.6411, longitude: -63.5852, duration: 120min">
                    //</div>
                    mapHTML = mapHTML 
                        + "<div " 
                            + " class='duration" 
                                    + parkingMeterArr[ i ].meterDuration 
                                    + "class'" 
                            + " style='position: absolute; " + positionCode + "'" 
                            + " title='" + title + "'>" 
                        + "</div>";
                }
                return mapHTML;
            }
            
            //INPUT EVENT: create the map when #buttonId is clicked
            document.querySelector( '#buttonId' ).onclick = function () {
                console.log( 'button has been clicked...' );
                
                var parkingMeterArr = initParkingMeterArr();
                var mapCode         = createMapCode( parkingMeterArr );
                outputMap( mapCode );
            }
        </script>
    </body>
</html>