diff --git a/OSM/OpenLayers/.eslintrc.js b/OSM/OpenLayers/.eslintrc.js new file mode 100644 index 0000000..fac3652 --- /dev/null +++ b/OSM/OpenLayers/.eslintrc.js @@ -0,0 +1,14 @@ +module.exports = { + env: { + browser: true, + es2021: true + }, + extends: [ + 'standard' + ], + parserOptions: { + ecmaVersion: 12 + }, + rules: { + } +} diff --git a/OSM/OpenLayers/main.js b/OSM/OpenLayers/main.js new file mode 100644 index 0000000..4349cdf --- /dev/null +++ b/OSM/OpenLayers/main.js @@ -0,0 +1,232 @@ + +const csv = '../Locator/input.csv' +// Initial zoom +const zoom = 12 + +// OpenLayers - https://openlayers.org/ + +function OLMap (ol) { + /* global OpenLayers */ + const mercator = OpenLayers.Projection.transforms['EPSG:4326']['EPSG:3857'] + + // Layer with map + const map = new OpenLayers.Map('map', { projection: 'EPSG:3857', controls: [] }) + const osm = new OpenLayers.Layer.OSM() + + const features = [] + const elements = [] + const lat = [] + const lon = [] + + const img = 'data:image/png;base64,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' + + let i = 0 + ol.forEach(element => { + features[i] = new OpenLayers.Feature.Vector( + mercator(new OpenLayers.Geometry.Point(element[2], element[1])), + { id: i }, + { + externalGraphic: img, + graphicWidth: 48, + graphicHeight: 48 + } + ) + + elements[i] = element[0] + lon[i] = element[2] + lat[i] = element[1] + i++ + }) + + // Get the center of the map + const center = mercator({ x: (max(lon) + min(lon)) / 2, y: (max(lat) + min(lat)) / 2 }) + + // Layer with listeners + const vector = new OpenLayers.Layer.Vector( + 'Points', + { + eventListeners: { + featureselected: function (evt) { + const feature = evt.feature + const id = feature.attributes.id + const address = elements[id].split(', ') + const popup = new OpenLayers.Popup( + 'popup', + OpenLayers.LonLat.fromString(feature.geometry.toShortString()), + null, + '
' + address[0] + '
' +
+ address[1] + '
' +
+ 'Lon: ' + lon[id] + '
' +
+ 'Lat: ' + lat[id] + '
',
+ false
+ )
+
+ feature.popup = popup
+ map.addPopup(popup)
+
+ // Popup style
+ let elem = document.getElementById('popup')
+ elem.style.border = '1px solid darkgrey'
+ elem.style.borderRadius = '15pt'
+ elem.style.width = '160pt'
+ elem.style.height = '70pt'
+ elem.style.padding = '1pt 5pt'
+ elem.style.margin = '20pt 0pt 0pt 0pt'
+ elem.style.fontFamily = "'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif"
+ elem.style.fontSize = '9pt'
+ elem = document.getElementById('popup_contentDiv')
+ elem.style.width = '160pt'
+ elem.style.height = '70pt'
+ },
+ // Destroy popup
+ featureunselected: function (evt) {
+ const feature = evt.feature
+ map.removePopup(feature.popup)
+ feature.popup.destroy()
+ feature.popup = null
+ }
+ }
+ }
+ )
+ vector.addFeatures(features)
+
+ // Select feature control
+ const selector = new OpenLayers.Control.SelectFeature(
+ vector,
+ {
+ toggle: true,
+ clickout: true,
+ autoActivate: true
+ }
+ )
+
+ map.addLayers([osm, vector])
+
+ map.addControl(new OpenLayers.Control.PanZoomBar())
+ map.addControl(new OpenLayers.Control.Navigation())
+ map.addControl(selector)
+ map.setCenter(new OpenLayers.LonLat(center.x, center.y), zoom)
+}
+
+fetch(csv).then(
+ function (r) {
+ r.text().then(
+ function (content) {
+ const rows = CSVToArray(content, ',')
+ const ol = []
+ rows.forEach(row => {
+ const item = []
+ row.forEach(element => {
+ const value = element.trim()
+ item.push(value)
+ })
+ ol.push(item)
+ })
+ OLMap(ol)
+ }
+ )
+ }
+)
+
+// Map style
+const elem = document.getElementById('map')
+elem.style.position = 'fixed'
+elem.style.top = '0pt'
+elem.style.left = '0pt'
+elem.style.width = '100%'
+elem.style.height = '100%'
+
+/*
+ * This will parse a delimited string into an array of arrays. The default delimiter is
+ * the comma, but this can be overriden in the second argument.
+ *
+ * ES6 version of this function: https://www.bennadel.com/blog/1504-ask-ben-parsing-csv-strings-with-javascript-exec-regular-expression-command.htm
+ *
+ */
+
+function CSVToArray (strData, strDelimiter) {
+ // Check to see if the delimiter is defined. If not,
+ // then default to comma.
+ strDelimiter = (strDelimiter || ',')
+
+ // Create a regular expression to parse the CSV values.
+ const objPattern = new RegExp(
+ (
+ // Delimiters.
+ '(\\' + strDelimiter + '|\\r?\\n|\\r|^)' +
+
+ // Quoted fields.
+ '(?:"([^"]*(?:""[^"]*)*)"|' +
+
+ // Standard fields.
+ '([^"\\' + strDelimiter + '\\r\\n]*))'
+ ),
+ 'gi'
+ )
+
+ // Create an array to hold our data. Give the array
+ // a default empty first row.
+ const arrData = [[]]
+
+ // Create an array to hold our individual pattern
+ // matching groups.
+ let arrMatches = null
+
+ let strMatchedValue = ''
+
+ // Keep looping over the regular expression matches
+ // until we can no longer find a match.
+ while ((arrMatches = objPattern.exec(strData))) {
+ // Get the delimiter that was found.
+ const strMatchedDelimiter = arrMatches[1]
+
+ // Check to see if the given delimiter has a length
+ // (is not the start of string) and if it matches
+ // field delimiter. If id does not, then we know
+ // that this delimiter is a row delimiter.
+ if (
+ strMatchedDelimiter.length &&
+ (strMatchedDelimiter !== strDelimiter)
+ ) {
+ // Since we have reached a new row of data,
+ // add an empty row to our data array.
+ arrData.push([])
+ }
+
+ // Now that we have our delimiter out of the way,
+ // let's check to see which kind of value we
+ // captured (quoted or unquoted).
+ if (arrMatches[2]) {
+ // We found a quoted value. When we capture
+ // this value, unescape any double quotes.
+ strMatchedValue = arrMatches[2].replace(
+ '/""/g',
+ '"'
+ )
+ } else {
+ // We found a non-quoted value.
+ strMatchedValue = arrMatches[3]
+ }
+
+ // Now that we have our value string, let's add
+ // it to the data array.
+ arrData[arrData.length - 1].push(strMatchedValue)
+ }
+
+ // Return the parsed data.
+ return (arrData)
+}
+
+function max (input) {
+ if (toString.call(input) !== '[object Array]') {
+ return false
+ }
+ return Math.max.apply(null, input)
+}
+
+function min (input) {
+ if (toString.call(input) !== '[object Array]') {
+ return false
+ }
+ return Math.min.apply(null, input)
+}
diff --git a/OSM/OpenLayers/map.html b/OSM/OpenLayers/map.html
new file mode 100644
index 0000000..639a7c2
--- /dev/null
+++ b/OSM/OpenLayers/map.html
@@ -0,0 +1,15 @@
+
+