From f33dd7e2643ff511082752a8b70e8a3d5b53b504 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=B6rl?= Date: Wed, 10 Jun 2020 21:28:41 +0200 Subject: [PATCH] Add activities layer --- .gitignore | 1 + backend/matsim.py | 35 +++++++ backend/run.py | 30 ++++++ src/App.vue | 33 +++++- src/components/ActivitiesLayer.vue | 133 ++++++++++++++++++++++++ src/components/ActivitiesLayerPanel.vue | 26 +++++ 6 files changed, 253 insertions(+), 5 deletions(-) create mode 100644 backend/matsim.py create mode 100644 src/components/ActivitiesLayer.vue create mode 100644 src/components/ActivitiesLayerPanel.vue diff --git a/.gitignore b/.gitignore index 5a26a7f..2f23396 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ data/* +__pycache__ .DS_Store node_modules diff --git a/backend/matsim.py b/backend/matsim.py new file mode 100644 index 0000000..35f7aa2 --- /dev/null +++ b/backend/matsim.py @@ -0,0 +1,35 @@ +import gzip +import xml.sax + +class PopulationReader(xml.sax.handler.ContentHandler): + def __init__(self): + self.population = [] + + self.person = None + self.process_activities = False + + def startElement(self, name, attributes): + if name == "person": + self.person = { "id": attributes["id"], "activities": [] } + self.process_activities = False + + elif name == "plan": + self.process_activities = attributes["selected"] == "yes" + + elif name == "activity": + self.person["activities"].append({ + "purpose": attributes["type"], + "x": float(attributes["x"]), + "y": float(attributes["y"]) + }) + + def endElement(self, name): + if name == "person": + self.population.append(self.person) + self.person = None + +def read_population(path): + with gzip.open(path) as f: + reader = PopulationReader() + xml.sax.parse(f, reader) + return reader.population diff --git a/backend/run.py b/backend/run.py index 59e4b27..ab45829 100644 --- a/backend/run.py +++ b/backend/run.py @@ -2,6 +2,7 @@ from flask_restful import Resource, Api from flask_cors import CORS from flask import request +import matsim app = Flask(__name__) CORS(app) @@ -38,6 +39,8 @@ df_services = df_services[["departure_time", "pickup_time", "origin_x", "origin_y"]] df_services["id"] = np.arange(len(df_services)) +population = matsim.read_population("../data/output_plans.xml.gz") + def gini(x): """Compute Gini coefficient of array of values""" diffsum = 0 @@ -49,6 +52,31 @@ class RequestsLayer(Resource): def get(self): return json.loads(df_services.to_json(orient = "records")) +class ActivitiesLayer(Resource): + def get(self, person_id): + for person in population: + if person["id"] == person_id: + return person["activities"] + +class PersonsLayer(Resource): + def get(self): + persons = [] + + for person in population: + for activity in person["activities"]: + if activity["purpose"] == "home": + distance = np.sqrt((activity["x"] - center.x)**2 + (activity["y"] - center.y)**2) + + if distance < radius: + persons.append(dict( + id = person["id"], + x = activity["x"], + y = activity["y"] + )) + break + + return persons + class PopulationLayer(Resource): def get(self, attribute, metric): if not attribute in ("age", "income"): @@ -92,6 +120,8 @@ def transform(iris_id, iris_name, population, shape): api.add_resource(Network, '/network') api.add_resource(RequestsLayer, '/requests') +api.add_resource(PersonsLayer, '/persons') +api.add_resource(ActivitiesLayer, '/activities/') api.add_resource(PopulationLayer, '/population//') if __name__ == '__main__': diff --git a/src/App.vue b/src/App.vue index 58ab7fa..a444180 100644 --- a/src/App.vue +++ b/src/App.vue @@ -3,19 +3,20 @@ eqarun - Runs Visualization - + + - + + @@ -31,13 +32,16 @@ import ZoneLayer from "./components/ZoneLayer.vue" import RequestsLayerPanel from "./components/RequestsLayerPanel.vue" import RequestsLayer from "./components/RequestsLayer.vue" +import ActivitiesLayerPanel from "./components/ActivitiesLayerPanel.vue" +import ActivitiesLayer from "./components/ActivitiesLayer.vue" + import * as axios from "axios"; import * as _ from "lodash"; export default { name: 'App', components: { - ZoneLayerPanel, ZoneLayer, RequestsLayerPanel, RequestsLayer + ZoneLayerPanel, ZoneLayer, RequestsLayerPanel, RequestsLayer, ActivitiesLayerPanel, ActivitiesLayer }, data() { var layerState = Vue.observable({ @@ -57,8 +61,14 @@ export default { scale: 0.028, offset: [0, 0] }) + var activitiesLayerState = Vue.observable({ + loading: false, + persons: [], selectedPerson: undefined, + scale: 0.028, offset: [0, 0] + }) + return { - layerState: layerState, requestsLayerState: requestsLayerState, + layerState: layerState, requestsLayerState: requestsLayerState, activitiesLayerState: activitiesLayerState, scale: 0.028, offset: [0, 0], mouseDownLocation: undefined }; @@ -66,6 +76,7 @@ export default { mounted() { this.load(); this.loadRequests(); + this.loadActivities(); }, methods: { load() { @@ -90,6 +101,16 @@ export default { this.requestsLayerState.loading = false; }); }, + loadActivities() { + this.activitiesLayerState.loading = true; + var url = window.location.protocol + "//" + window.location.hostname + ":5000"; + + axios.get( + url + "/persons").then((response) => { + this.activitiesLayerState.persons = response.data; + this.activitiesLayerState.loading = false; + }); + }, onScale(event) { this.scale -= 1e-3 * event.deltaY; this.updateScale(); @@ -97,6 +118,7 @@ export default { updateScale: _.debounce(function() { this.layerState.scale = this.scale; this.requestsLayerState.scale = this.scale; + this.activitiesLayerState.scale = this.scale; }, 100), onMouseDown(event) { this.mouseDownLocation = [event.clientX, event.clientY]; @@ -113,6 +135,7 @@ export default { updateOffset: _.debounce(function() { this.layerState.offset = this.offset; this.requestsLayerState.offset = this.offset; + this.activitiesLayerState.offset = this.offset; }, 100), }, watch: { diff --git a/src/components/ActivitiesLayer.vue b/src/components/ActivitiesLayer.vue new file mode 100644 index 0000000..f3458ae --- /dev/null +++ b/src/components/ActivitiesLayer.vue @@ -0,0 +1,133 @@ + + + + + diff --git a/src/components/ActivitiesLayerPanel.vue b/src/components/ActivitiesLayerPanel.vue new file mode 100644 index 0000000..afbd377 --- /dev/null +++ b/src/components/ActivitiesLayerPanel.vue @@ -0,0 +1,26 @@ + + +