<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:
-
Your users will be using different screen sizes and shapes to
view your map,
which means you will have to read the screen dimensions
in order to automatically adjust the size of the map.
You can use the
getScreenInformation()
function to read the screen sizes. It returns the screen sizes as an object with.screenHeight
and.screenWidth
properties. -
It would be nice to analyse the screen dimensions and
the data's minimums and maximums
in order to automatically scale the map to the user at the largest size.
This is a math and logic challenge, and I have created
a few functions that can take care of this for you:
getMinsAndMaxs()
, andcalculatePositionCode()
.
The mathematics of screen scaling is about using ratios: if you like, take a look at the functions to see how it is done. Or, ignore the math and just use the code.
Once you have these tools, we can create a map with the following procedure:
- Declare and initialize the map data as an array of objects having x (longitude) and y (latitude) properties.
- Read the screen dimensions.
-
Discover the minimums and maximums of your
.longitude
and.latitude
data. - Loop through each element of the array.
- Each element of the array is an object, and it has object properties that you can access.
-
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>
. -
Change the class or style of that
<div>
in order to reflect the other data of the objects, in this case, the.meterDuration
property. -
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>