Skip to content

Commit

Permalink
Merge pull request #45 from CenterForDigitalHumanities/issue_20
Browse files Browse the repository at this point in the history
Starting zoom
  • Loading branch information
yashaswini-slu authored Apr 19, 2024
2 parents 8cf2f51 + 4b0d2ab commit 4679a53
Showing 1 changed file with 162 additions and 5 deletions.
167 changes: 162 additions & 5 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,146 @@ VIEWER.isJSON = function(obj) {
return r
}

/**
* Parse through the given JSON object and keep track of how many navplace objects are in it.
* Saves all the navplace instances in an array
* If the search gets too deep, it can take very many minutes, so there is a 'depth' parameter in consideration of time
*/
VIEWER.navplaceObject = (function(geojson, navplaces, depth) {
//make sure value is a dict and not null
if (typeof geojson !== 'object' || geojson === null) {
return undefined
}
//find navplace in the current object
if (geojson.hasOwnProperty('navPlace')) {
navplaces.push(geojson['navPlace'])
}

if (depth > 5) {
return undefined
}
//recursive
for (let key in geojson) {
let value = geojson[key]

if (Array.isArray(value)) {
for (var i = 0; i < value.length; i++) {
if (typeof value[i] === 'object' || Array.isArray(value[i])) {
this.navplaceObject(value[i], navplaces, depth+1)
}
}
} else if (typeof value === 'object') {
this.navplaceObject(value, navplaces, depth+1)
}
}
return navplaces.filter(item => item !== undefined)
})

/**
* Looks at all navplace geometries, and fits a minimizing bounding box around them.
* Returns furtherst west and east longitudes, and furthest north and south latitudes, creating a box.
* First checks if navplace oject is a Feature Collection or a Feature.
* If a FC, it recursively calls the getBbox function on each of the features within the FC.
* If a Feature, checks the geometry type and then collects all of the coordinates.
* Finally, returns the mins and maxes of all the lats and longs.
*/
VIEWER.getBbox = (function(navplaceObj){
if ((navplaceObj['type'] == 'FeatureCollection')) {
let features = navplaceObj['features']
let count = features.length
let swlat = undefined
let swlon = undefined
let nelat = undefined
let nelon = undefined
for (let i=0; i < count; i++){
let bbox = this.getBbox(features[i])
let _swlat = bbox[1]
let _swlon = bbox[0]
let _nelat = bbox[3]
let _nelon = bbox[2]
if ((! swlat) || (_swlat < swlat)){
swlat = _swlat
}
if ((! swlon) || (_swlon < swlon)){
swlon = _swlon
}
if ((! nelat) || (_nelat > nelat)){
nelat = _nelat
}
if ((! nelon) || (_nelon > nelon)){
nelon = _nelon
}
}
return [ swlon, swlat, nelon, nelat ]
}
else if (navplaceObj['type'] == 'Feature'){
// Adapted from http://gis.stackexchange.com/a/172561
// See also: https://tools.ietf.org/html/rfc7946#section-3.1
let geom = navplaceObj['geometry']
let coords = geom.coordinates
let lats = [], lngs = []
if (geom.type == 'Point') {
return [ coords[0], coords[1], coords[0], coords[1] ]
} else if (geom.type == 'MultiPoint' || geom.type == 'LineString') {
for (let i = 0; i < coords.length; i++) {
lats.push(coords[i][1])
lngs.push(coords[i][0])
}
} else if (geom.type == 'MultiLineString') {
for (let i = 0; i < coords.length; i++) {
for (let j = 0; j < coords[i].length; j++) {
lats.push(coords[i][j][1])
lngs.push(coords[i][j][0])
}
}
} else if (geom.type == 'Polygon') {
for (let i = 0; i < coords[0].length; i++) {
lats.push(coords[0][i][1])
lngs.push(coords[0][i][0])
}
} else if (geom.type == 'MultiPolygon') {
for (let i = 0; i < coords.length; i++) {
for (let j = 0; j < coords[i][0].length; j++) {
lats.push(coords[i][0][j][1])
lngs.push(coords[i][0][j][0])
}
}
}
const minlat = Math.min.apply(null, lats),
maxlat = Math.max.apply(null, lats)
const minlng = Math.min.apply(null, lngs),
maxlng = Math.max.apply(null, lngs)
return [ minlng, minlat, maxlng, maxlat ]
}
})

/**
* Given the bounding Box of the geometries, calculate the appropriate zoom
* Find height and width of the box, and check several cases.
* log2 is used to find the appropriate zoom. We then want the minimum zoom to make sure to include everything.
*/
VIEWER.calculateZoom = function(bbox){
const boundsWidth = Math.abs(bbox[2] - bbox[0]) //lng
const boundsHeight = Math.abs(bbox[3] - bbox[1]) //lat

if (boundsWidth === 0 && boundsHeight === 0){ //fixed at 8 if it's just one point
return 8

} else if (boundsWidth === 0) { // just return vertical zoom if a straight vertical line
let zoomY = Math.floor(Math.log2(256 / boundsWidth))
return Math.min(zoomY)

} else if (boundsHeight === 0) { // just return horizontal zoom is a straight horizontal line
let zoomX = Math.floor(Math.log2(256 / boundsHeight))
return Math.min(zoomX)

} else { // for all other geometry cases
let zoomX = Math.floor(Math.log2(256 / boundsWidth))
let zoomY = Math.floor(Math.log2(256 / boundsHeight))
return Math.min(zoomX, zoomY)
}
}

/**
* Search all levels of the JSON for all navPlace properties. Annotation bodies do not recurse this way.
* If you come across a referenced value, attempt to dereference it. If successful, embed it to go forward with (so as not to resolve it again)
Expand Down Expand Up @@ -683,7 +823,6 @@ VIEWER.init = async function() {
if(location.pathname.includes("annotation-viewer")) VIEWER.supportedTypes = Array.from(VIEWER.annotationTypes)
else{ VIEWER.supportedTypes = Array.from(VIEWER.iiifResourceTypes) }

let latlong = [12, 12] //default starting coords
let geos = []
let resource = {}
let geoJsonData = []
Expand All @@ -710,7 +849,25 @@ VIEWER.init = async function() {
}
let formattedGeoJsonData = geoJsonData.flat(1) //AnnotationPages and FeatureCollections cause arrays in arrays.
//Abstracted. Maybe one day you want to VIEWER.initializeOtherWebMap(latlong, allGeos)
VIEWER.initializeLeaflet(latlong, formattedGeoJsonData)
//Initialize some default values, used if >1 navplace object.
const navplaces = VIEWER.navplaceObject(VIEWER.resource, [], 1)
let zoomLevel = 0
let centerCoords = [0,0]
if (navplaces.length == 0) { //If there is no navplace, or a bad URI, just display an empty map.
VIEWER.initializeLeaflet(centerCoords, 2)
}
else { // otherwise, fixed no zoom and centered.
if (navplaces.length == 1) { //if just one navplace, calculate these values
let bbox = VIEWER.getBbox(navplaces[0])
centerCoords = [((bbox[1]+bbox[3])/2.0), (bbox[0]+bbox[2])/2.0]
zoomLevel = VIEWER.calculateZoom(bbox)
}
//Zoomlevel should have a minimum of 2. Also zooms in by 2 because default zoom is far out.
zoomLevel = zoomLevel + 2
zoomLevel = Math.max(zoomLevel, 2)
VIEWER.initializeLeaflet(centerCoords, zoomLevel, formattedGeoJsonData)
}

}

/**
Expand All @@ -720,7 +877,7 @@ VIEWER.init = async function() {
* All Features from the outer most objects and their children are present.
* This may have caused duplicates in some cases.
*/
VIEWER.initializeLeaflet = async function(coords, geoMarkers) {
VIEWER.initializeLeaflet = async function(coords, zoom, geoMarkers) {

let mapbox_satellite_layer=
L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoidGhlaGFiZXMiLCJhIjoiY2pyaTdmNGUzMzQwdDQzcGRwd21ieHF3NCJ9.SSflgKbI8tLQOo2DuzEgRQ', {
Expand Down Expand Up @@ -755,7 +912,7 @@ VIEWER.initializeLeaflet = async function(coords, geoMarkers) {

VIEWER.mymap = L.map('leafletInstanceContainer', {
center: coords,
zoom: 2,
zoom: zoom,
layers: [osm, esri_street, topomap, mapbox_satellite_layer]
})

Expand All @@ -775,7 +932,7 @@ VIEWER.initializeLeaflet = async function(coords, geoMarkers) {
// "Streets": esri_street,
// "Satellite" : mapbox_satellite_layer,
// "Topography" : topomap
// };
// }
//var layerControl = L.control.layers(baseMaps, overlayMaps).addTo(VIEWER.mymap)

let appColor = "#008080"
Expand Down

0 comments on commit 4679a53

Please sign in to comment.