add hotspotsRIT page

This commit is contained in:
2023-10-24 17:06:48 -04:00
parent 0f116ecaf5
commit 43d9812406
7 changed files with 2283 additions and 0 deletions

View File

@@ -1,6 +1,7 @@
import flask
from flask_minify import Minify
import json
from tasks import TaskHandler
proj = json.load(open("./static/json/projects.json", "r"))
books = json.load(open("./static/json/books.json", "r"))
@@ -12,6 +13,7 @@ pages['home']['books'] = books
pages['books']['books'] = books
app = flask.Flask(__name__)
tasks = TaskHandler()
@app.route('/api/goto/')
@@ -33,6 +35,18 @@ for i in pages:
def resume():
return flask.send_file("./static/Resume.pdf")
@app.route("/hotspots")
def hotspotsRIT():
return flask.render_template("hotspots.html")
@app.route("/hotspotsrit/cached")
def getCached():
return json.dumps(tasks.getCache())
@app.route("/hotspotsrit/current")
def getLive():
return json.dumps(tasks.getCurrent())
@app.errorhandler(Exception)
def page404(e):

View File

@@ -0,0 +1,25 @@
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%);
}

430
src/static/js/hotspots.js Normal file
View File

@@ -0,0 +1,430 @@
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: '&copy; <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:
'&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors &copy; <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: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors &copy; <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: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors &copy; <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;
ratio = ratio > 1 ? 1 : ratio;
let red = 255 * ratio;
let style = { fillColor: `rgba(${red}, 0, 0, ${ratio})` };
ref.setStyle(style);
ref.bindPopup(
`${attrs.properties.name}<br />Current Occupation: ${attrs.properties.count}`
);
}
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 = {
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;
const densityMapUrl = "/hotspotsrit" // https://maps.rit.edu/proxySearch/densityMapDetail.php?mdo=1
async function init() {
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;
}
}
});
let ptsLayer = L.geoJSON(Object.values(pts), {
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,
}).addTo(map);
const features = ptsLayer.getLayers();
for (let i = 0; i < features.length; i++) {
const key = features[i].feature.properties.mdo_id;
pts[key].properties.reference = features[i];
}
}
// let laid = L.geoJson(pts).addTo(map)
// laid.remove()
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) {
options["color"] = "rgba(190, 95, 0, 0.2)";
options.fade = true;
options.fadeSpeed = 60000 * 15;
arcGen(fromC, toC, (options = options));
}
}
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,
animate: 500,
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)
);
});
});
}
const space = [43.09224, -77.674799];
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`
);
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, space]);
x.properties.diff--;
}
while (x.properties.diff < 0) {
shots.push([space, x]);
x.properties.diff++;
}
});
return shots;
}
init().then(() => {
// map.on("click", () => {
// shootVector(pts[2], pts[8]);
// });
// shootVector(pts[0], pts[1], {speed: 500});
calcDistances(Object.values(pts));
getUpdate()
setInterval(getUpdate, 60000 * 5);
});

View File

@@ -0,0 +1,558 @@
/*
* 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 Normal file
View File

@@ -0,0 +1,57 @@
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

View File

@@ -0,0 +1,21 @@
<!DOCTYPE html>
<head>
<link rel="stylesheet" href="{{ url_for('static', filename='css/hotspots.css') }}" />
<link
rel="stylesheet"
href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
crossorigin=""
/>
<script
src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
crossorigin=""
></script>
<script src="{{ url_for('static', filename='js/lib/leaflet-providers.js') }}"></script>
<script src="{{ url_for('static', filename='js/lib/CUSTOM.leaflet.curve.js') }}"></script>
</head>
<body>
<div id="map"></div>
<script src="{{ url_for('static', filename='js/hotspots.js') }}"></script>
</body>