mirror of
https://github.com/asimonson1125/asimonson1125.github.io.git
synced 2026-02-25 05:09:49 -06:00
Move hotspots to separate service
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,4 +1,5 @@
|
|||||||
.venv
|
.venv
|
||||||
__pycache__
|
__pycache__
|
||||||
notes.txt
|
notes.txt
|
||||||
react_OLD
|
react_OLD
|
||||||
|
envs.py
|
||||||
29
src/app.py
29
src/app.py
@@ -1,8 +1,9 @@
|
|||||||
import flask
|
import flask
|
||||||
from flask_minify import Minify
|
from flask_minify import Minify
|
||||||
import json
|
import json
|
||||||
from tasks import TaskHandler
|
|
||||||
import werkzeug.exceptions as HTTPerror
|
import werkzeug.exceptions as HTTPerror
|
||||||
|
import requests
|
||||||
|
from config import *
|
||||||
|
|
||||||
proj = json.load(open("./static/json/projects.json", "r"))
|
proj = json.load(open("./static/json/projects.json", "r"))
|
||||||
books = json.load(open("./static/json/books.json", "r"))
|
books = json.load(open("./static/json/books.json", "r"))
|
||||||
@@ -16,8 +17,6 @@ pages['home']['books'] = books
|
|||||||
pages['books']['books'] = books
|
pages['books']['books'] = books
|
||||||
|
|
||||||
app = flask.Flask(__name__)
|
app = flask.Flask(__name__)
|
||||||
tasks = TaskHandler()
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/goto/')
|
@app.route('/api/goto/')
|
||||||
@app.route('/api/goto/<location>')
|
@app.route('/api/goto/<location>')
|
||||||
@@ -32,11 +31,6 @@ def goto(location='home'):
|
|||||||
page = page404(e)
|
page = page404(e)
|
||||||
return [pagevars, page]
|
return [pagevars, page]
|
||||||
|
|
||||||
# I am literally insane
|
|
||||||
# There was no reason for me to do this
|
|
||||||
# it saved some lines of code I guess
|
|
||||||
# infinite flaskless flask here we comes
|
|
||||||
|
|
||||||
def funcGen(pagename, pages):
|
def funcGen(pagename, pages):
|
||||||
def dynamicRule():
|
def dynamicRule():
|
||||||
try:
|
try:
|
||||||
@@ -62,16 +56,17 @@ def resume():
|
|||||||
|
|
||||||
@app.route("/hotspots")
|
@app.route("/hotspots")
|
||||||
def hotspotsRIT():
|
def hotspotsRIT():
|
||||||
return flask.render_template("hotspots.html")
|
pagevars = {
|
||||||
|
"template": "iframe.html",
|
||||||
@app.route("/hotspotsrit/cached")
|
"title": f"Hotspots @ RIT",
|
||||||
def getCached():
|
"description": "Hotspots @ RIT by Andrew Simonson",
|
||||||
return json.dumps(tasks.getCache())
|
"canonical": "/hotspots",
|
||||||
|
}
|
||||||
@app.route("/hotspotsrit/current")
|
return flask.render_template("iframe.html", url=HotspotsURL, var=pagevars)
|
||||||
def getLive():
|
|
||||||
return json.dumps(tasks.getCurrent())
|
|
||||||
|
|
||||||
|
@app.route("/hotspots/<path>")
|
||||||
|
def hotspotsProxy(path):
|
||||||
|
return requests.get(f"{HotspotsURL}/{path}").content
|
||||||
|
|
||||||
@app.errorhandler(Exception)
|
@app.errorhandler(Exception)
|
||||||
def page404(e):
|
def page404(e):
|
||||||
|
|||||||
8
src/config.py
Normal file
8
src/config.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
from os import environ as env
|
||||||
|
# automatically updates some dev envs. need to remove for production.
|
||||||
|
try:
|
||||||
|
__import__('envs.py')
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
HotspotsURL = env.get('HotspotsURL', 'https://asimonson.com/hotspots')
|
||||||
@@ -38,6 +38,11 @@ body {
|
|||||||
font-family: "Roboto Condensed", sans-serif;
|
font-family: "Roboto Condensed", sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#fullIframe {
|
||||||
|
width:100vw;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
#menu {
|
#menu {
|
||||||
height: 2rem;
|
height: 2rem;
|
||||||
display: none;
|
display: none;
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
overflow: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
#map {
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
html, body, #map {
|
|
||||||
height: 100%;
|
|
||||||
width: 100vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
.leaflet-layer,
|
|
||||||
.leaflet-control-zoom-in,
|
|
||||||
.leaflet-control-zoom-out,
|
|
||||||
.leaflet-control-attribution {
|
|
||||||
filter: brightness(500%) contrast(130%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.leaflet-tile-pane {
|
|
||||||
filter: brightness(50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.legend {
|
|
||||||
border: solid #999999 3px;
|
|
||||||
color: #eee;
|
|
||||||
background-color: rgba(44, 44, 44, .8);
|
|
||||||
padding: 1em;
|
|
||||||
border-radius: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#legendOccGrad {
|
|
||||||
background-image: linear-gradient(to right, transparent, red);
|
|
||||||
padding: 0 2em;
|
|
||||||
text-align: center;
|
|
||||||
text-shadow: black .2em .2em;
|
|
||||||
}
|
|
||||||
@@ -1,550 +0,0 @@
|
|||||||
let map = L.map("map", {
|
|
||||||
zoomControl: false,
|
|
||||||
attributionControl: false,
|
|
||||||
}).setView([43.084679, -77.674702], 17);
|
|
||||||
// L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
||||||
// maxZoom: 19,
|
|
||||||
// attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
|
|
||||||
// }).addTo(map);
|
|
||||||
|
|
||||||
var CartoDB_DarkMatterNoLabels = L.tileLayer(
|
|
||||||
"https://{s}.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}{r}.png",
|
|
||||||
{
|
|
||||||
attribution:
|
|
||||||
'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors © <a href="https://carto.com/attributions">CARTO</a>',
|
|
||||||
subdomains: "abcd",
|
|
||||||
maxZoom: 20,
|
|
||||||
}
|
|
||||||
).addTo(map);
|
|
||||||
|
|
||||||
// var CartoDB_PositronNoLabels = L.tileLayer('https://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png', {
|
|
||||||
// attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors © <a href="https://carto.com/attributions">CARTO</a>',
|
|
||||||
// subdomains: 'abcd',
|
|
||||||
// maxZoom: 20
|
|
||||||
// }).addTo(map); // good hacky filter: invert(100%) hue-rotate(180deg) brightness(100%) contrast(100%);
|
|
||||||
|
|
||||||
// var CartoDB_Positron = L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', {
|
|
||||||
// attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors © <a href="https://carto.com/attributions">CARTO</a>',
|
|
||||||
// subdomains: 'abcd',
|
|
||||||
// maxZoom: 20
|
|
||||||
// }).addTo(map);
|
|
||||||
|
|
||||||
function sleep(ms) {
|
|
||||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
||||||
}
|
|
||||||
|
|
||||||
function shuffle(array) {
|
|
||||||
let currentIndex = array.length,
|
|
||||||
randomIndex;
|
|
||||||
|
|
||||||
// While there remain elements to shuffle.
|
|
||||||
while (currentIndex > 0) {
|
|
||||||
// Pick a remaining element.
|
|
||||||
randomIndex = Math.floor(Math.random() * currentIndex);
|
|
||||||
currentIndex--;
|
|
||||||
|
|
||||||
// And swap it with the current element.
|
|
||||||
[array[currentIndex], array[randomIndex]] = [
|
|
||||||
array[randomIndex],
|
|
||||||
array[currentIndex],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setMarker(attrs, ref) {
|
|
||||||
// let red = parseInt("ff", 16);
|
|
||||||
// let green = parseInt("78", 16);
|
|
||||||
// let style = {"fillColor": `#${red.toString(16)}${green.toString(16)}00`};
|
|
||||||
let ratio = attrs.properties.count / attrs.properties.capacity;
|
|
||||||
let adjustedratio = ratio > 1 ? 1 : ratio;
|
|
||||||
let red = 255 * adjustedratio;
|
|
||||||
let style = { fillColor: `rgba(255, 0, 0, ${adjustedratio})` };
|
|
||||||
ref.setStyle(style);
|
|
||||||
ref.bindPopup(
|
|
||||||
`${attrs.properties.name}<br />Current Occupation: ${
|
|
||||||
attrs.properties.count
|
|
||||||
}<br />${Math.round(ratio * 100)}% capacity`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onEachFeature(feature, layer) {
|
|
||||||
// does this feature have a property named popupContent?
|
|
||||||
if (!feature.properties || !feature.properties.name) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setMarker(feature, layer);
|
|
||||||
}
|
|
||||||
|
|
||||||
const polyStyle = {
|
|
||||||
color: "#ff7800",
|
|
||||||
weight: 5,
|
|
||||||
opacity: 0.65,
|
|
||||||
};
|
|
||||||
|
|
||||||
const geojsonMarkerOptions = {
|
|
||||||
pane: "nodePane",
|
|
||||||
radius: 8,
|
|
||||||
fillColor: "#ff7800",
|
|
||||||
color: "#ff7800",
|
|
||||||
weight: 3,
|
|
||||||
opacity: 1,
|
|
||||||
fillOpacity: 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
const pointStyle = {};
|
|
||||||
|
|
||||||
function ritCustomize(input) {
|
|
||||||
badOnes = [166]; // Nathan's (166) is a duplicate of Ben and Jerry's
|
|
||||||
for (let i = input.length - 1; i >= 0; i--) {
|
|
||||||
if (badOnes.indexOf(input[i].mdo_id) >= 0) {
|
|
||||||
input.splice(i, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unused: "Campus", "Gleason_Engineering_Student_Area"
|
|
||||||
const no_mdo_ids = {
|
|
||||||
Library_A_Level: { type: "Point", coordinates: [-77.676355, 43.083974] },
|
|
||||||
Library_1st_Floor: { type: "Point", coordinates: [-77.676355, 43.083874] },
|
|
||||||
Library_2nd_Floor: { type: "Point", coordinates: [-77.676355, 43.083774] },
|
|
||||||
Library_3rd_Floor: { type: "Point", coordinates: [-77.676355, 43.083674] },
|
|
||||||
Library_4th_Floor: { type: "Point", coordinates: [-77.676355, 43.083574] },
|
|
||||||
Ross_Hall: { type: "Point", coordinates: [-77.677937, 43.082379] },
|
|
||||||
Gordon_Field_House: { type: "Point", coordinates: [-77.671725, 43.085149] },
|
|
||||||
Golisano_Institute_for_Sustainability_Lobby: {
|
|
||||||
type: "Point",
|
|
||||||
coordinates: [-77.681365, 43.085376],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
function ritCustomizeCoords(input) {
|
|
||||||
try {
|
|
||||||
if (input.properties.name == "Beanz") {
|
|
||||||
input.geometry.coordinates = [-77.66904, 43.083876];
|
|
||||||
}
|
|
||||||
return input;
|
|
||||||
} catch {}
|
|
||||||
try {
|
|
||||||
if (no_mdo_ids[input.location] == undefined) return;
|
|
||||||
let geojsonObj = {
|
|
||||||
geometry: no_mdo_ids[input.location],
|
|
||||||
properties: {
|
|
||||||
mdo_id: input.location,
|
|
||||||
name: input.location.replaceAll("_", " "),
|
|
||||||
},
|
|
||||||
type: "Feature",
|
|
||||||
};
|
|
||||||
return geojsonObj;
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
|
|
||||||
let pts;
|
|
||||||
let nodePane = map.createPane("nodePane");
|
|
||||||
nodePane.style.zIndex = "600";
|
|
||||||
let nodeGroup;
|
|
||||||
const densityMapUrl = "/hotspotsrit"; // https://maps.rit.edu/proxySearch/densityMapDetail.php?mdo=1
|
|
||||||
|
|
||||||
async function init(legend = false) {
|
|
||||||
let counts = fetch(densityMapUrl + "/cached");
|
|
||||||
|
|
||||||
let locations = fetch(
|
|
||||||
"https://maps.rit.edu/proxySearch/locations.search.php"
|
|
||||||
);
|
|
||||||
|
|
||||||
counts = Object.values(await (await counts).json());
|
|
||||||
counts = ritCustomize(counts);
|
|
||||||
locations = await (await locations).json();
|
|
||||||
|
|
||||||
pts = {};
|
|
||||||
locations.forEach((x) => {
|
|
||||||
for (let i = 0; i < counts.length; i++) {
|
|
||||||
if (counts[i].mdo_id == x.properties.mdo_id) {
|
|
||||||
x.properties.count = counts[i].count;
|
|
||||||
x.properties.capacity =
|
|
||||||
counts[i].max_occ == null ? 100 : counts[i].max_occ;
|
|
||||||
x = ritCustomizeCoords(x);
|
|
||||||
pts[x.properties.mdo_id] = x;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
counts.forEach((x) => {
|
|
||||||
if (pts[x.mdo_id] == undefined) {
|
|
||||||
let geojson = ritCustomizeCoords(x);
|
|
||||||
if (geojson !== undefined) {
|
|
||||||
geojson.properties.count = x.count;
|
|
||||||
geojson.properties.capacity = x.max_occ == null ? 100 : x.max_occ;
|
|
||||||
pts[x.location] = geojson;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
nodeGroup = L.geoJSON(Object.values(pts), {
|
|
||||||
pane: "nodePane",
|
|
||||||
pointToLayer: function (feature, latlng) {
|
|
||||||
return L.circleMarker(latlng, geojsonMarkerOptions);
|
|
||||||
},
|
|
||||||
style: function (feature) {
|
|
||||||
switch (feature.geometry.type) {
|
|
||||||
case "Polygon":
|
|
||||||
return polyStyle;
|
|
||||||
case "Point":
|
|
||||||
return pointStyle;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onEachFeature: onEachFeature,
|
|
||||||
});
|
|
||||||
nodeGroup.addTo(map);
|
|
||||||
nodeGroup.bringToFront();
|
|
||||||
|
|
||||||
const features = nodeGroup.getLayers();
|
|
||||||
for (let i = 0; i < features.length; i++) {
|
|
||||||
const key = features[i].feature.properties.mdo_id;
|
|
||||||
pts[key].properties.reference = features[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
legend ? makeLegend() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeLegend() {
|
|
||||||
let legend = L.control({ position: "bottomright" });
|
|
||||||
|
|
||||||
legend.onAdd = function (mapref) {
|
|
||||||
let div = L.DomUtil.create("div", "info legend");
|
|
||||||
div.innerHTML =
|
|
||||||
"<div id='legendOccGrad'>Occupancy / Max Occupancy Gradient</div>";
|
|
||||||
div.innerHTML += `<p>Markers represent locations where data is collected<br />
|
|
||||||
Vectors represent the migration of aggregate occupation</br>
|
|
||||||
vectors to/from nodeless points involve locations not tracked by RIT</p>`;
|
|
||||||
|
|
||||||
return div;
|
|
||||||
};
|
|
||||||
|
|
||||||
let statControl = L.control({ position: "topright" });
|
|
||||||
|
|
||||||
statControl.onAdd = function (mapref) {
|
|
||||||
let div = L.DomUtil.create("div", "info legend");
|
|
||||||
div.innerHTML =
|
|
||||||
"<p>Occupancy is updated every 5 minutes<br /><br /><span id='shotCounter'></span><br />Next update in <span id='countdownClock'></span> seconds</p>";
|
|
||||||
return div;
|
|
||||||
//
|
|
||||||
};
|
|
||||||
|
|
||||||
statControl.addTo(map);
|
|
||||||
legend.addTo(map);
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateLegend(shotcount = undefined) {
|
|
||||||
if (!useLegend) return;
|
|
||||||
try {
|
|
||||||
document.getElementById(
|
|
||||||
"shotCounter"
|
|
||||||
).textContent = `Previous update created ${shotcount} migrations`;
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
|
|
||||||
const space_coords = [43.09224, -77.674799];
|
|
||||||
const UC_coords = [43.080361, -77.683296];
|
|
||||||
const perkins_coords = [43.08616, -77.661796];
|
|
||||||
function setSpace(features) {
|
|
||||||
features.forEach((x) => {
|
|
||||||
const centroid = getCoordArray(x);
|
|
||||||
if (centroid[1] > -77.673157) {
|
|
||||||
x.properties.space = perkins_coords;
|
|
||||||
} else if (centroid[1] < -77.677503 && centroid[0] < 43.08395) {
|
|
||||||
x.properties.space = UC_coords;
|
|
||||||
} else {
|
|
||||||
x.properties.space = space_coords;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let bullets = L.layerGroup([]);
|
|
||||||
async function shootVector(
|
|
||||||
from,
|
|
||||||
to,
|
|
||||||
{ speed = 500, color = null, onlyAnimate = true, trail = true } = {}
|
|
||||||
) {
|
|
||||||
options = {
|
|
||||||
onlyAnimate: onlyAnimate,
|
|
||||||
animate: {
|
|
||||||
duration: speed,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
if (color) options.color = color;
|
|
||||||
const fromC = getCoordArray(from);
|
|
||||||
const toC = getCoordArray(to);
|
|
||||||
arcGen(fromC, toC, (options = options));
|
|
||||||
|
|
||||||
if (trail) {
|
|
||||||
updateTrailers(from, to);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let trailers = [];
|
|
||||||
let lastTrailerUpdate = new Date().getTime();
|
|
||||||
async function updateTrailers(from = undefined, to = undefined) {
|
|
||||||
let found = false;
|
|
||||||
if (from !== undefined && to !== undefined) {
|
|
||||||
// check if vector already exists and increase count
|
|
||||||
trailers.forEach((x) => {
|
|
||||||
if (x.from == from && x.to == to) {
|
|
||||||
found = true;
|
|
||||||
x.count += 1;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (!found) {
|
|
||||||
let newtrail = arcGen(getCoordArray(from), getCoordArray(to), {
|
|
||||||
color: "rgb(152,76, 0)",
|
|
||||||
});
|
|
||||||
trailers.push({ to: to, from: from, count: 1, ref: newtrail });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't want this calculation killing the processor, so it can only happen once every 5 seconds
|
|
||||||
// unless there's a new vector to the party so its opacity gets set
|
|
||||||
const now = new Date().getTime();
|
|
||||||
if (found && now - lastTrailerUpdate < 5000) {
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
lastTrailerUpdate = now;
|
|
||||||
}
|
|
||||||
|
|
||||||
// weight brightness of vectors
|
|
||||||
let sum = 0;
|
|
||||||
const trailCount = trailers.length;
|
|
||||||
let max = 10;
|
|
||||||
trailers.forEach((x) => {
|
|
||||||
sum += x.count;
|
|
||||||
if (x.count > max) max = x.count;
|
|
||||||
});
|
|
||||||
trailers.forEach((x, i) => {
|
|
||||||
let opacity = Math.sqrt(x.count / max);
|
|
||||||
x.ref._path.style.opacity = opacity;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCoordArray(ref) {
|
|
||||||
if (ref.properties == undefined) return ref;
|
|
||||||
let coords;
|
|
||||||
try {
|
|
||||||
coords = ref.properties.reference.getLatLng();
|
|
||||||
} catch {
|
|
||||||
coords = ref.properties.reference.getBounds().getCenter();
|
|
||||||
}
|
|
||||||
return [coords.lat, coords.lng];
|
|
||||||
}
|
|
||||||
|
|
||||||
function arcGen(latlng1, latlng2, options = {}) {
|
|
||||||
var latlngs = [];
|
|
||||||
|
|
||||||
var offsetX = latlng2[1] - latlng1[1],
|
|
||||||
offsetY = latlng2[0] - latlng1[0];
|
|
||||||
|
|
||||||
var r = Math.sqrt(Math.pow(offsetX, 2) + Math.pow(offsetY, 2)),
|
|
||||||
theta = Math.atan2(offsetY, offsetX);
|
|
||||||
|
|
||||||
var thetaOffset = 3.14 / 10;
|
|
||||||
|
|
||||||
var r2 = r / 2 / Math.cos(thetaOffset),
|
|
||||||
theta2 = theta + thetaOffset;
|
|
||||||
|
|
||||||
var midpointX = r2 * Math.cos(theta2) + latlng1[1],
|
|
||||||
midpointY = r2 * Math.sin(theta2) + latlng1[0];
|
|
||||||
|
|
||||||
var midpointLatLng = [midpointY, midpointX];
|
|
||||||
|
|
||||||
latlngs.push(latlng1, midpointLatLng, latlng2);
|
|
||||||
|
|
||||||
var pathDefaults = {
|
|
||||||
color: "#b35900",
|
|
||||||
weight: 3,
|
|
||||||
hasBalls: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
let pathOptions = Object.assign(pathDefaults, options);
|
|
||||||
|
|
||||||
var curvedPath = L.curve(
|
|
||||||
["M", latlng1, "Q", midpointLatLng, latlng2],
|
|
||||||
pathOptions
|
|
||||||
).addTo(map);
|
|
||||||
|
|
||||||
return curvedPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
function calcDistances(nodes) {
|
|
||||||
nodes.forEach((x) => {
|
|
||||||
x.properties.distances = {};
|
|
||||||
const coords1 = getCoordArray(x);
|
|
||||||
nodes.forEach((y) => {
|
|
||||||
const coords2 = getCoordArray(y);
|
|
||||||
x.properties.distances[y.properties.mdo_id] = Math.sqrt(
|
|
||||||
Math.pow(coords1[0] - coords2[0], 2) +
|
|
||||||
Math.pow(coords1[1] - coords2[1], 2)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let countdownTo = new Date().getTime() + 5 * 60 * 1000;
|
|
||||||
function updateCountdown() {
|
|
||||||
const now = new Date().getTime();
|
|
||||||
const countdown = countdownTo - now;
|
|
||||||
if (countdown < 1000) {
|
|
||||||
countdownTo = now + 5 * 60 * 1000;
|
|
||||||
getUpdate();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
document.getElementById("countdownClock").textContent = Math.floor(
|
|
||||||
countdown / 1000
|
|
||||||
);
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getUpdate() {
|
|
||||||
console.log("Updating Occupancy Matrix");
|
|
||||||
|
|
||||||
let counts = await fetch(densityMapUrl + "/current");
|
|
||||||
counts = await counts.json();
|
|
||||||
|
|
||||||
for (let i = 0; i < counts.length; i++) {
|
|
||||||
const pt =
|
|
||||||
counts[i].mdo_id == null
|
|
||||||
? pts[counts[i].location]
|
|
||||||
: pts[counts[i].mdo_id];
|
|
||||||
if (pt == undefined) continue;
|
|
||||||
pt.properties.diff = counts[i].count - pt.properties.count;
|
|
||||||
pt.properties.count = counts[i].count;
|
|
||||||
setMarker(pt, pt.properties.reference);
|
|
||||||
}
|
|
||||||
|
|
||||||
let shots = getShots(Object.values(pts));
|
|
||||||
shots = shuffle(shuffle(shots));
|
|
||||||
const timeBetween = (60000 * 5 + 1) / shots.length;
|
|
||||||
|
|
||||||
// randomize time delay
|
|
||||||
let timeDelay = [];
|
|
||||||
shots.forEach(() => {
|
|
||||||
timeDelay.push(Math.random());
|
|
||||||
});
|
|
||||||
const interval = 60000 * 5; // 5 minute delay
|
|
||||||
for (let i = 0; i < timeDelay.length; i++) {
|
|
||||||
timeDelay[i] = timeDelay[i] * interval;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
`Shot total for next 5 minutes: ${shots.length} - ${
|
|
||||||
timeBetween / 1000
|
|
||||||
} second intervals`
|
|
||||||
);
|
|
||||||
updateLegend(shots.length);
|
|
||||||
for (let i = 0; i < shots.length; i++) {
|
|
||||||
loadShot(shots[i], timeDelay[i], { trail: true });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadShot(shot, delay, { trail = false } = {}) {
|
|
||||||
await sleep(delay);
|
|
||||||
shootVector(shot[0], shot[1], { trail: trail });
|
|
||||||
}
|
|
||||||
|
|
||||||
function findOneShot(nodes, target) {
|
|
||||||
let sortedByDistance = nodes.sort((a, b) => {
|
|
||||||
return (
|
|
||||||
target.properties.distances[a.properties.mdo_id] -
|
|
||||||
target.properties.distances[b.properties.mdo_id]
|
|
||||||
);
|
|
||||||
});
|
|
||||||
const sign = target.properties.diff > 0;
|
|
||||||
for (let x = 1; x < sortedByDistance.length; x++) {
|
|
||||||
if (sortedByDistance[x].properties.diff > 0 !== sign) {
|
|
||||||
return sortedByDistance[x];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getShots(nodes) {
|
|
||||||
let noChange = false;
|
|
||||||
let shots = [];
|
|
||||||
let sourcesAndSinks = nodes.filter((x) => {
|
|
||||||
return x.properties.diff !== 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
let i;
|
|
||||||
while (!noChange && sourcesAndSinks.length > 0) {
|
|
||||||
noChange = true;
|
|
||||||
// sourcesAndSinks.forEach((x) => {
|
|
||||||
// x.properties.changed = false;
|
|
||||||
// });
|
|
||||||
// for (let i = sourcesAndSinks.length - 1; i >= 0; i--) {
|
|
||||||
// try {
|
|
||||||
// if (sourcesAndSinks[i].properties.changed) continue; // this node is a prior recipient this iteration
|
|
||||||
// } catch {
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
let sorted = sourcesAndSinks.sort((a, b) => {
|
|
||||||
Math.abs(a.properties.diff) - Math.abs(b.properties.diff);
|
|
||||||
});
|
|
||||||
i = sourcesAndSinks.indexOf(sorted[0]);
|
|
||||||
let recipient = findOneShot(sourcesAndSinks, sourcesAndSinks[i]);
|
|
||||||
if (recipient) {
|
|
||||||
let shotArr;
|
|
||||||
if (sourcesAndSinks[i].properties.diff > 0) {
|
|
||||||
shotArr = [sourcesAndSinks[i], recipient];
|
|
||||||
sourcesAndSinks[i].properties.diff--;
|
|
||||||
recipient.properties.diff++;
|
|
||||||
} else {
|
|
||||||
shotArr = [recipient, sourcesAndSinks[i]];
|
|
||||||
sourcesAndSinks[i].properties.diff++;
|
|
||||||
recipient.properties.diff--;
|
|
||||||
}
|
|
||||||
shots.push(shotArr);
|
|
||||||
noChange = false;
|
|
||||||
sourcesAndSinks[i].properties.changed = true;
|
|
||||||
recipient.properties.changed = true;
|
|
||||||
|
|
||||||
let tmpRef = recipient;
|
|
||||||
if (sourcesAndSinks[i].properties.diff == 0) {
|
|
||||||
sourcesAndSinks.splice(i, 1);
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
sourcesAndSinks[sourcesAndSinks.indexOf(tmpRef)].properties.diff == 0
|
|
||||||
) {
|
|
||||||
sourcesAndSinks.splice(recipient, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
// if no more people on campus, get them from space
|
|
||||||
sourcesAndSinks.forEach((x) => {
|
|
||||||
while (x.properties.diff > 0) {
|
|
||||||
shots.push([x, x.properties.space]);
|
|
||||||
x.properties.diff--;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (x.properties.diff < 0) {
|
|
||||||
shots.push([x.properties.space, x]);
|
|
||||||
x.properties.diff++;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return shots;
|
|
||||||
}
|
|
||||||
|
|
||||||
const useLegend = window.location.pathname.replaceAll("/", "") == "hotspots";
|
|
||||||
init(useLegend).then(() => {
|
|
||||||
// map.on("click", () => {
|
|
||||||
// shootVector(pts[2], pts[8]);
|
|
||||||
// });
|
|
||||||
// shootVector(pts[0], pts[1], {speed: 500});
|
|
||||||
let ptsarr = Object.values(pts);
|
|
||||||
calcDistances(ptsarr);
|
|
||||||
setSpace(ptsarr);
|
|
||||||
getUpdate();
|
|
||||||
setInterval(updateCountdown, 1000);
|
|
||||||
});
|
|
||||||
@@ -1,558 +0,0 @@
|
|||||||
/*
|
|
||||||
* Leaflet.curve v0.9.2 - a plugin for Leaflet mapping library. https://github.com/elfalem/Leaflet.curve
|
|
||||||
* (c) elfalem 2015-2023
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
* note that SVG (x, y) corresponds to (long, lat)
|
|
||||||
*/
|
|
||||||
|
|
||||||
L.Curve = L.Path.extend({
|
|
||||||
options: {},
|
|
||||||
|
|
||||||
initialize: function (path, options) {
|
|
||||||
L.setOptions(this, options);
|
|
||||||
this._setPath(path);
|
|
||||||
},
|
|
||||||
|
|
||||||
// Added to follow the naming convention of L.Polyline and other Leaflet component classes:
|
|
||||||
// (https://leafletjs.com/reference-1.6.0.html#polyline-setlatlngs)
|
|
||||||
setLatLngs: function (path) {
|
|
||||||
return this.setPath(path);
|
|
||||||
},
|
|
||||||
|
|
||||||
getLatLngs: function () {
|
|
||||||
return this.getPath();
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateBounds: function () {
|
|
||||||
var tolerance = this._clickTolerance();
|
|
||||||
var tolerancePoint = new L.Point(tolerance, tolerance);
|
|
||||||
|
|
||||||
//_pxBounds is critical for canvas renderer, used to determine area that needs redrawing
|
|
||||||
this._pxBounds = new L.Bounds([
|
|
||||||
this._rawPxBounds.min.subtract(tolerancePoint),
|
|
||||||
this._rawPxBounds.max.add(tolerancePoint),
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
|
|
||||||
getPath: function () {
|
|
||||||
return this._coords;
|
|
||||||
},
|
|
||||||
|
|
||||||
setPath: function (path) {
|
|
||||||
this._setPath(path);
|
|
||||||
return this.redraw();
|
|
||||||
},
|
|
||||||
|
|
||||||
getBounds: function () {
|
|
||||||
return this._bounds;
|
|
||||||
},
|
|
||||||
|
|
||||||
_setPath: function (path) {
|
|
||||||
this._coords = path;
|
|
||||||
this._bounds = this._computeBounds();
|
|
||||||
},
|
|
||||||
|
|
||||||
_computeBounds: function () {
|
|
||||||
var bound = new L.LatLngBounds();
|
|
||||||
var lastPoint;
|
|
||||||
var lastCommand;
|
|
||||||
var coord;
|
|
||||||
for (var i = 0; i < this._coords.length; i++) {
|
|
||||||
coord = this._coords[i];
|
|
||||||
if (typeof coord == "string" || coord instanceof String) {
|
|
||||||
lastCommand = coord;
|
|
||||||
} else if (lastCommand == "H") {
|
|
||||||
bound.extend([lastPoint.lat, coord[0]]);
|
|
||||||
lastPoint = new L.latLng(lastPoint.lat, coord[0]);
|
|
||||||
} else if (lastCommand == "V") {
|
|
||||||
bound.extend([coord[0], lastPoint.lng]);
|
|
||||||
lastPoint = new L.latLng(coord[0], lastPoint.lng);
|
|
||||||
} else if (lastCommand == "C") {
|
|
||||||
var controlPoint1 = new L.latLng(coord[0], coord[1]);
|
|
||||||
coord = this._coords[++i];
|
|
||||||
var controlPoint2 = new L.latLng(coord[0], coord[1]);
|
|
||||||
coord = this._coords[++i];
|
|
||||||
var endPoint = new L.latLng(coord[0], coord[1]);
|
|
||||||
|
|
||||||
bound.extend(controlPoint1);
|
|
||||||
bound.extend(controlPoint2);
|
|
||||||
bound.extend(endPoint);
|
|
||||||
|
|
||||||
endPoint.controlPoint1 = controlPoint1;
|
|
||||||
endPoint.controlPoint2 = controlPoint2;
|
|
||||||
lastPoint = endPoint;
|
|
||||||
} else if (lastCommand == "S") {
|
|
||||||
var controlPoint2 = new L.latLng(coord[0], coord[1]);
|
|
||||||
coord = this._coords[++i];
|
|
||||||
var endPoint = new L.latLng(coord[0], coord[1]);
|
|
||||||
|
|
||||||
var controlPoint1 = lastPoint;
|
|
||||||
if (lastPoint.controlPoint2) {
|
|
||||||
var diffLat = lastPoint.lat - lastPoint.controlPoint2.lat;
|
|
||||||
var diffLng = lastPoint.lng - lastPoint.controlPoint2.lng;
|
|
||||||
controlPoint1 = new L.latLng(
|
|
||||||
lastPoint.lat + diffLat,
|
|
||||||
lastPoint.lng + diffLng
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
bound.extend(controlPoint1);
|
|
||||||
bound.extend(controlPoint2);
|
|
||||||
bound.extend(endPoint);
|
|
||||||
|
|
||||||
endPoint.controlPoint1 = controlPoint1;
|
|
||||||
endPoint.controlPoint2 = controlPoint2;
|
|
||||||
lastPoint = endPoint;
|
|
||||||
} else if (lastCommand == "Q") {
|
|
||||||
var controlPoint = new L.latLng(coord[0], coord[1]);
|
|
||||||
coord = this._coords[++i];
|
|
||||||
var endPoint = new L.latLng(coord[0], coord[1]);
|
|
||||||
|
|
||||||
bound.extend(controlPoint);
|
|
||||||
bound.extend(endPoint);
|
|
||||||
|
|
||||||
endPoint.controlPoint = controlPoint;
|
|
||||||
lastPoint = endPoint;
|
|
||||||
} else if (lastCommand == "T") {
|
|
||||||
var endPoint = new L.latLng(coord[0], coord[1]);
|
|
||||||
|
|
||||||
var controlPoint = lastPoint;
|
|
||||||
if (lastPoint.controlPoint) {
|
|
||||||
var diffLat = lastPoint.lat - lastPoint.controlPoint.lat;
|
|
||||||
var diffLng = lastPoint.lng - lastPoint.controlPoint.lng;
|
|
||||||
controlPoint = new L.latLng(
|
|
||||||
lastPoint.lat + diffLat,
|
|
||||||
lastPoint.lng + diffLng
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
bound.extend(controlPoint);
|
|
||||||
bound.extend(endPoint);
|
|
||||||
|
|
||||||
endPoint.controlPoint = controlPoint;
|
|
||||||
lastPoint = endPoint;
|
|
||||||
} else {
|
|
||||||
bound.extend(coord);
|
|
||||||
lastPoint = new L.latLng(coord[0], coord[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bound;
|
|
||||||
},
|
|
||||||
|
|
||||||
getCenter: function () {
|
|
||||||
return this._bounds.getCenter();
|
|
||||||
},
|
|
||||||
|
|
||||||
// _update() is invoked by Path._reset()
|
|
||||||
_update: function () {
|
|
||||||
if (!this._map) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: consider implementing this._clipPoints(); and this._simplifyPoints(); to improve performance
|
|
||||||
this._updatePath();
|
|
||||||
},
|
|
||||||
|
|
||||||
_updatePath: function () {
|
|
||||||
// the following can be thought of as this._renderer.updateCurve() in both SVG/Canvas renderers
|
|
||||||
// similar to Canvas._updatePoly(), Canvas._updateCircle(), etc...
|
|
||||||
if (this._usingCanvas) {
|
|
||||||
this._updateCurveCanvas();
|
|
||||||
} else {
|
|
||||||
this._updateCurveSvg();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
//_project() is invoked by Path._reset()
|
|
||||||
_project: function () {
|
|
||||||
var coord, lastCoord, curCommand, curPoint;
|
|
||||||
|
|
||||||
this._points = [];
|
|
||||||
|
|
||||||
for (var i = 0; i < this._coords.length; i++) {
|
|
||||||
coord = this._coords[i];
|
|
||||||
if (typeof coord == "string" || coord instanceof String) {
|
|
||||||
this._points.push(coord);
|
|
||||||
curCommand = coord;
|
|
||||||
} else {
|
|
||||||
switch (coord.length) {
|
|
||||||
case 2:
|
|
||||||
curPoint = this._map.latLngToLayerPoint(coord);
|
|
||||||
lastCoord = coord;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
if (curCommand == "H") {
|
|
||||||
curPoint = this._map.latLngToLayerPoint([lastCoord[0], coord[0]]);
|
|
||||||
lastCoord = [lastCoord[0], coord[0]];
|
|
||||||
} else {
|
|
||||||
curPoint = this._map.latLngToLayerPoint([coord[0], lastCoord[1]]);
|
|
||||||
lastCoord = [coord[0], lastCoord[1]];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
this._points.push(curPoint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._bounds.isValid()) {
|
|
||||||
var northWestLayerPoint = this._map.latLngToLayerPoint(
|
|
||||||
this._bounds.getNorthWest()
|
|
||||||
);
|
|
||||||
var southEastLayerPoint = this._map.latLngToLayerPoint(
|
|
||||||
this._bounds.getSouthEast()
|
|
||||||
);
|
|
||||||
this._rawPxBounds = new L.Bounds(
|
|
||||||
northWestLayerPoint,
|
|
||||||
southEastLayerPoint
|
|
||||||
);
|
|
||||||
this._updateBounds();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_curvePointsToPath: function (points) {
|
|
||||||
var point,
|
|
||||||
curCommand,
|
|
||||||
str = "";
|
|
||||||
for (var i = 0; i < points.length; i++) {
|
|
||||||
point = points[i];
|
|
||||||
if (typeof point == "string" || point instanceof String) {
|
|
||||||
curCommand = point;
|
|
||||||
str += curCommand;
|
|
||||||
} else {
|
|
||||||
switch (curCommand) {
|
|
||||||
case "H":
|
|
||||||
str += point.x + " ";
|
|
||||||
break;
|
|
||||||
case "V":
|
|
||||||
str += point.y + " ";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
str += point.x + "," + point.y + " ";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return str || "M0 0";
|
|
||||||
},
|
|
||||||
|
|
||||||
beforeAdd: function (map) {
|
|
||||||
L.Path.prototype.beforeAdd.call(this, map);
|
|
||||||
|
|
||||||
this._usingCanvas = this._renderer instanceof L.Canvas;
|
|
||||||
|
|
||||||
if (this._usingCanvas) {
|
|
||||||
this._pathSvgElement = document.createElementNS(
|
|
||||||
"http://www.w3.org/2000/svg",
|
|
||||||
"path"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onAdd: function (map) {
|
|
||||||
if (this._usingCanvas) {
|
|
||||||
// determine if dash array is set by user
|
|
||||||
this._canvasSetDashArray = !this.options.dashArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
L.Path.prototype.onAdd.call(this, map); // calls _update()
|
|
||||||
|
|
||||||
if (this._usingCanvas) {
|
|
||||||
if (this.options.animate && typeof TWEEN === "object") {
|
|
||||||
this._normalizeCanvasAnimationOptions();
|
|
||||||
|
|
||||||
this._tweenedObject = { offset: this._pathSvgElement.getTotalLength() };
|
|
||||||
this._tween = new TWEEN.Tween(this._tweenedObject)
|
|
||||||
.to({ offset: 0 }, this.options.animate.duration)
|
|
||||||
// difference of behavior with SVG, delay occurs on every iteration
|
|
||||||
.delay(this.options.animate.delay)
|
|
||||||
.repeat(this.options.animate.iterations - 1)
|
|
||||||
.onComplete(
|
|
||||||
(function (scope) {
|
|
||||||
return function () {
|
|
||||||
scope._canvasAnimating = false;
|
|
||||||
};
|
|
||||||
})(this)
|
|
||||||
)
|
|
||||||
.start();
|
|
||||||
|
|
||||||
this._canvasAnimating = true;
|
|
||||||
this._animateCanvas();
|
|
||||||
} else {
|
|
||||||
this._canvasAnimating = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (this.options.animate && this._path.animate) {
|
|
||||||
var length = Math.min(this._svgSetDashArray(), 1000);
|
|
||||||
this._path.pathLength.baseVal = length;
|
|
||||||
keyframes = [
|
|
||||||
{ strokeDashoffset: length },
|
|
||||||
{ strokeDashoffset: 0 }
|
|
||||||
]
|
|
||||||
if (!this.options.fade) keyframes.push({ strokeDashoffset: -length });
|
|
||||||
let animation = this._path.animate(
|
|
||||||
keyframes,
|
|
||||||
this.options.animate
|
|
||||||
);
|
|
||||||
if (this.options.fade) {
|
|
||||||
animation.onfinish = () => {
|
|
||||||
this._path.animate(
|
|
||||||
[{ opacity: 1 }, { opacity: 0 }],
|
|
||||||
this.options.fadeSpeed
|
|
||||||
).onfinish = () => { this.remove()};
|
|
||||||
};
|
|
||||||
} else if (this.options.onlyAnimate) {
|
|
||||||
animation.onfinish = () => {
|
|
||||||
this.remove();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// SVG specific logic
|
|
||||||
_updateCurveSvg: function () {
|
|
||||||
this._renderer._setPath(this, this._curvePointsToPath(this._points));
|
|
||||||
|
|
||||||
if (this.options.animate) {
|
|
||||||
this._svgSetDashArray();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_svgSetDashArray: function () {
|
|
||||||
var path = this._path;
|
|
||||||
var length = path.getTotalLength();
|
|
||||||
|
|
||||||
if (!this.options.dashArray) {
|
|
||||||
path.style.strokeDasharray = length + " " + length;
|
|
||||||
}
|
|
||||||
return length;
|
|
||||||
},
|
|
||||||
|
|
||||||
// Needed by the `Canvas` renderer for interactivity
|
|
||||||
_containsPoint: function (layerPoint) {
|
|
||||||
if (!this._bounds.isValid()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return this._bounds.contains(this._map.layerPointToLatLng(layerPoint));
|
|
||||||
},
|
|
||||||
|
|
||||||
// Canvas specific logic below here
|
|
||||||
_normalizeCanvasAnimationOptions: function () {
|
|
||||||
var opts = {
|
|
||||||
delay: 0,
|
|
||||||
duration: 0,
|
|
||||||
iterations: 1,
|
|
||||||
};
|
|
||||||
if (typeof this.options.animate == "number") {
|
|
||||||
opts.duration = this.options.animate;
|
|
||||||
} else {
|
|
||||||
if (this.options.animate.duration) {
|
|
||||||
opts.duration = this.options.animate.duration;
|
|
||||||
}
|
|
||||||
if (this.options.animate.delay) {
|
|
||||||
opts.delay = this.options.animate.delay;
|
|
||||||
}
|
|
||||||
if (this.options.animate.iterations) {
|
|
||||||
opts.iterations = this.options.animate.iterations;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.options.animate = opts;
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateCurveCanvas: function () {
|
|
||||||
var pathString = this._curvePointsToPath(this._points);
|
|
||||||
this._pathSvgElement.setAttribute("d", pathString);
|
|
||||||
|
|
||||||
if (
|
|
||||||
this.options.animate &&
|
|
||||||
typeof TWEEN === "object" &&
|
|
||||||
this._canvasSetDashArray
|
|
||||||
) {
|
|
||||||
this.options.dashArray = this._pathSvgElement.getTotalLength() + "";
|
|
||||||
this._renderer._updateDashArray(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._curveFillStroke(new Path2D(pathString), this._renderer._ctx);
|
|
||||||
},
|
|
||||||
|
|
||||||
_animateCanvas: function () {
|
|
||||||
TWEEN.update();
|
|
||||||
|
|
||||||
// clear out area and re-render all layers
|
|
||||||
this._renderer._updatePaths();
|
|
||||||
|
|
||||||
if (this._canvasAnimating) {
|
|
||||||
this._animationFrameId = L.Util.requestAnimFrame(
|
|
||||||
this._animateCanvas,
|
|
||||||
this
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// similar to Canvas._fillStroke(ctx, layer)
|
|
||||||
_curveFillStroke: function (path2d, ctx) {
|
|
||||||
ctx.lineDashOffset = this._canvasAnimating
|
|
||||||
? this._tweenedObject.offset
|
|
||||||
: 0.0;
|
|
||||||
|
|
||||||
var options = this.options;
|
|
||||||
|
|
||||||
if (options.fill) {
|
|
||||||
ctx.globalAlpha = options.fillOpacity;
|
|
||||||
ctx.fillStyle = options.fillColor || options.color;
|
|
||||||
ctx.fill(path2d, options.fillRule || "evenodd");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.stroke && options.weight !== 0) {
|
|
||||||
if (ctx.setLineDash) {
|
|
||||||
ctx.setLineDash((this.options && this.options._dashArray) || []);
|
|
||||||
}
|
|
||||||
ctx.globalAlpha = options.opacity;
|
|
||||||
ctx.lineWidth = options.weight;
|
|
||||||
ctx.strokeStyle = options.color;
|
|
||||||
ctx.lineCap = options.lineCap;
|
|
||||||
ctx.lineJoin = options.lineJoin;
|
|
||||||
ctx.stroke(path2d);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// path tracing logic below here
|
|
||||||
trace: function (t) {
|
|
||||||
// initially map is undefined, but then null if curve was added and removed
|
|
||||||
if (this._map === undefined || this._map === null) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
t = t.filter(function (element) {
|
|
||||||
return element >= 0 && element <= 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
var point, curCommand, curStartPoint, curEndPoint;
|
|
||||||
var p1, p2, p3;
|
|
||||||
var samples = [];
|
|
||||||
for (var i = 0; i < this._points.length; i++) {
|
|
||||||
point = this._points[i];
|
|
||||||
if (typeof point == "string" || point instanceof String) {
|
|
||||||
curCommand = point;
|
|
||||||
|
|
||||||
if (curCommand == "Z") {
|
|
||||||
samples = samples.concat(
|
|
||||||
this._linearTrace(t, curEndPoint, curStartPoint)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch (curCommand) {
|
|
||||||
case "M":
|
|
||||||
curStartPoint = point;
|
|
||||||
curEndPoint = point;
|
|
||||||
break;
|
|
||||||
case "L":
|
|
||||||
case "H":
|
|
||||||
case "V":
|
|
||||||
samples = samples.concat(this._linearTrace(t, curEndPoint, point));
|
|
||||||
|
|
||||||
curEndPoint = point;
|
|
||||||
break;
|
|
||||||
case "C":
|
|
||||||
p1 = point;
|
|
||||||
p2 = this._points[++i];
|
|
||||||
p3 = this._points[++i];
|
|
||||||
samples = samples.concat(
|
|
||||||
this._cubicTrace(t, curEndPoint, p1, p2, p3)
|
|
||||||
);
|
|
||||||
|
|
||||||
curEndPoint = p3;
|
|
||||||
break;
|
|
||||||
case "S":
|
|
||||||
p1 = this._reflectPoint(p2, curEndPoint);
|
|
||||||
p2 = point;
|
|
||||||
p3 = this._points[++i];
|
|
||||||
samples = samples.concat(
|
|
||||||
this._cubicTrace(t, curEndPoint, p1, p2, p3)
|
|
||||||
);
|
|
||||||
|
|
||||||
curEndPoint = p3;
|
|
||||||
break;
|
|
||||||
case "Q":
|
|
||||||
p1 = point;
|
|
||||||
p2 = this._points[++i];
|
|
||||||
samples = samples.concat(
|
|
||||||
this._quadraticTrace(t, curEndPoint, p1, p2)
|
|
||||||
);
|
|
||||||
|
|
||||||
curEndPoint = p2;
|
|
||||||
break;
|
|
||||||
case "T":
|
|
||||||
p1 = this._reflectPoint(p1, curEndPoint);
|
|
||||||
p2 = point;
|
|
||||||
samples = samples.concat(
|
|
||||||
this._quadraticTrace(t, curEndPoint, p1, p2)
|
|
||||||
);
|
|
||||||
|
|
||||||
curEndPoint = p2;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return samples;
|
|
||||||
},
|
|
||||||
_linearTrace: function (t, p0, p1) {
|
|
||||||
return t.map((interval) => {
|
|
||||||
var x = this._singleLinearTrace(interval, p0.x, p1.x);
|
|
||||||
var y = this._singleLinearTrace(interval, p0.y, p1.y);
|
|
||||||
return this._map.layerPointToLatLng([x, y]);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
_quadraticTrace: function (t, p0, p1, p2) {
|
|
||||||
return t.map((interval) => {
|
|
||||||
var x = this._singleQuadraticTrace(interval, p0.x, p1.x, p2.x);
|
|
||||||
var y = this._singleQuadraticTrace(interval, p0.y, p1.y, p2.y);
|
|
||||||
return this._map.layerPointToLatLng([x, y]);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
_cubicTrace: function (t, p0, p1, p2, p3) {
|
|
||||||
return t.map((interval) => {
|
|
||||||
var x = this._singleCubicTrace(interval, p0.x, p1.x, p2.x, p3.x);
|
|
||||||
var y = this._singleCubicTrace(interval, p0.y, p1.y, p2.y, p3.y);
|
|
||||||
return this._map.layerPointToLatLng([x, y]);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
_singleLinearTrace: function (t, p0, p1) {
|
|
||||||
return p0 + t * (p1 - p0);
|
|
||||||
},
|
|
||||||
_singleQuadraticTrace: function (t, p0, p1, p2) {
|
|
||||||
var oneMinusT = 1 - t;
|
|
||||||
return (
|
|
||||||
Math.pow(oneMinusT, 2) * p0 + 2 * oneMinusT * t * p1 + Math.pow(t, 2) * p2
|
|
||||||
);
|
|
||||||
},
|
|
||||||
_singleCubicTrace: function (t, p0, p1, p2, p3) {
|
|
||||||
var oneMinusT = 1 - t;
|
|
||||||
return (
|
|
||||||
Math.pow(oneMinusT, 3) * p0 +
|
|
||||||
3 * Math.pow(oneMinusT, 2) * t * p1 +
|
|
||||||
3 * oneMinusT * Math.pow(t, 2) * p2 +
|
|
||||||
Math.pow(t, 3) * p3
|
|
||||||
);
|
|
||||||
},
|
|
||||||
_reflectPoint: function (point, over) {
|
|
||||||
x = over.x + (over.x - point.x);
|
|
||||||
y = over.y + (over.y - point.y);
|
|
||||||
return L.point(x, y);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
L.curve = function (path, options) {
|
|
||||||
return new L.Curve(path, options);
|
|
||||||
};
|
|
||||||
|
|
||||||
// async function killMe(promise, obj){
|
|
||||||
// await promise;
|
|
||||||
// // obj.remove();
|
|
||||||
// console.log("killed");
|
|
||||||
// console.log(promise);
|
|
||||||
// }
|
|
||||||
File diff suppressed because it is too large
Load Diff
57
src/tasks.py
57
src/tasks.py
@@ -1,57 +0,0 @@
|
|||||||
import requests
|
|
||||||
import threading
|
|
||||||
import json
|
|
||||||
|
|
||||||
|
|
||||||
class TaskHandler:
|
|
||||||
def __init__(self):
|
|
||||||
self.hRIT_delayed = {}
|
|
||||||
self.hRIT_current = {}
|
|
||||||
self.updateCache()
|
|
||||||
TaskHandler.set_interval(self.updateCache, 60*5)
|
|
||||||
|
|
||||||
# Start HotspotsRIT webworker
|
|
||||||
# Source for corrections: I made it up
|
|
||||||
corrections = {
|
|
||||||
"Library_3rd_Floor": {"max_occ": 550},
|
|
||||||
"Library_2nd_Floor": {"max_occ": 250},
|
|
||||||
"Library_1st_Floor": {"max_occ": 350},
|
|
||||||
"Library_4th_Floor": {"max_occ": 400},
|
|
||||||
"Library_A_Level": {"max_occ": 300},
|
|
||||||
"Ross_Hall": {"max_occ": 200},
|
|
||||||
"Gordon_Field_House": {"max_occ": 750}
|
|
||||||
}
|
|
||||||
|
|
||||||
def getCache(self):
|
|
||||||
return self.hRIT_delayed
|
|
||||||
|
|
||||||
def getCurrent(self):
|
|
||||||
return self.hRIT_current
|
|
||||||
|
|
||||||
def updateCache(self):
|
|
||||||
r = requests.get("https://maps.rit.edu/proxySearch/densityMapDetail.php?mdo=1")
|
|
||||||
if r.status_code == 200:
|
|
||||||
if self.hRIT_current == {}:
|
|
||||||
self.hRIT_delayed = TaskHandler.dataAdjustments(r.json())
|
|
||||||
else:
|
|
||||||
self.hRIT_delayed = json.loads(json.dumps(self.hRIT_current)) # deepcopy was returning a function for some reason
|
|
||||||
self.hRIT_current = TaskHandler.dataAdjustments(r.json())
|
|
||||||
return self.hRIT_delayed, self.hRIT_current
|
|
||||||
else:
|
|
||||||
print("FUCK!", r.status_code)
|
|
||||||
|
|
||||||
def dataAdjustments(data):
|
|
||||||
for dp in data:
|
|
||||||
if dp['location'] in TaskHandler.corrections:
|
|
||||||
for correction in TaskHandler.corrections[dp['location']]:
|
|
||||||
dp[correction] = TaskHandler.corrections[dp['location']][correction]
|
|
||||||
return data
|
|
||||||
|
|
||||||
def set_interval(func, sec):
|
|
||||||
def func_wrapper():
|
|
||||||
TaskHandler.set_interval(func, sec)
|
|
||||||
func()
|
|
||||||
t = threading.Timer(sec, func_wrapper)
|
|
||||||
t.start()
|
|
||||||
return t
|
|
||||||
|
|
||||||
@@ -39,10 +39,6 @@
|
|||||||
|
|
||||||
gtag("config", "G-E2V93W9CNV");
|
gtag("config", "G-E2V93W9CNV");
|
||||||
</script>
|
</script>
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
href="{{ url_for('static', filename='css/hotspots.css') }}"
|
|
||||||
/>
|
|
||||||
<link
|
<link
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
href="{{ url_for('static', filename='css/App.css') }}"
|
href="{{ url_for('static', filename='css/App.css') }}"
|
||||||
@@ -57,29 +53,15 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<link rel="canonical" href="https://asimonson.com{{ var['canonical'] }}" />
|
<link rel="canonical" href="https://asimonson.com{{ var['canonical'] }}" />
|
||||||
<link defer
|
|
||||||
rel="stylesheet"
|
|
||||||
href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
|
|
||||||
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
|
|
||||||
crossorigin=""
|
|
||||||
/>
|
|
||||||
<script defer
|
|
||||||
src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
|
|
||||||
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
|
|
||||||
crossorigin=""
|
|
||||||
></script>
|
|
||||||
<script defer src="{{ url_for('static', filename='js/lib/leaflet-providers.js') }}"></script>
|
|
||||||
<script defer src="{{ url_for('static', filename='js/lib/CUSTOM.leaflet.curve.js') }}"></script>
|
|
||||||
<script src="{{ url_for('static', filename='js/checkbox.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/checkbox.js') }}"></script>
|
||||||
<script src="{{ url_for('static', filename='js/responsive.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/responsive.js') }}"></script>
|
||||||
<script src="{{ url_for('static', filename='js/chessbed.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/chessbed.js') }}"></script>
|
||||||
<title>{{ var['title'] }}</title>
|
<title>{{ var['title'] }}</title>
|
||||||
</head>
|
</head>
|
||||||
|
{% block header %}
|
||||||
<body onpopstate="backButton()">
|
<body onpopstate="backButton()">
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
<div id="map"></div>
|
<iframe src="/hotspots" title="HotspotsRIT" id="map"></iframe>
|
||||||
<script defer src="{{ url_for('static', filename='js/hotspots.js') }}"></script>
|
|
||||||
<div class="App">
|
<div class="App">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div id="name-container" onclick="goto('home', {toggle:false})">
|
<div id="name-container" onclick="goto('home', {toggle:false})">
|
||||||
@@ -115,7 +97,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
{% block content%}
|
||||||
<div id="root">{% include var['template'] %}</div>
|
<div id="root">{% include var['template'] %}</div>
|
||||||
|
{% endblock %}
|
||||||
|
{% block footer %}
|
||||||
<div class="footer">{% include 'partials/socials.html' %}</div>
|
<div class="footer">{% include 'partials/socials.html' %}</div>
|
||||||
|
{% endblock %}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
4
src/templates/iframe.html
Normal file
4
src/templates/iframe.html
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{% extends "header.html" %}
|
||||||
|
{% block header %}{% endblock %}
|
||||||
|
{% block footer %}{% endblock %}
|
||||||
|
{% block content %}<iframe id="fullIframe" src="{{ url }}" title="HotspotsRIT"></iframe>{% endblock %}
|
||||||
Reference in New Issue
Block a user