initial
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
*.sqlite3
|
||||||
|
*.swp
|
||||||
|
*.pyc
|
||||||
|
*.swo
|
||||||
|
env/
|
||||||
|
*/migrations/
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
"""
|
||||||
|
Django settings for bloomhunt project.
|
||||||
|
|
||||||
|
Generated by 'django-admin startproject' using Django 1.11.
|
||||||
|
|
||||||
|
For more information on this file, see
|
||||||
|
https://docs.djangoproject.com/en/1.11/topics/settings/
|
||||||
|
|
||||||
|
For the full list of settings and their values, see
|
||||||
|
https://docs.djangoproject.com/en/1.11/ref/settings/
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||||
|
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
|
||||||
|
|
||||||
|
# Quick-start development settings - unsuitable for production
|
||||||
|
# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/
|
||||||
|
|
||||||
|
# SECURITY WARNING: keep the secret key used in production secret!
|
||||||
|
SECRET_KEY = '8$19!k4+%bg^r0v#04+ksm2ny(4*dp!v8dxnbbz)sj275(_(^&'
|
||||||
|
|
||||||
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
|
DEBUG = True
|
||||||
|
|
||||||
|
ALLOWED_HOSTS = []
|
||||||
|
|
||||||
|
|
||||||
|
# Application definition
|
||||||
|
|
||||||
|
INSTALLED_APPS = [
|
||||||
|
'mobile',
|
||||||
|
'map',
|
||||||
|
'django.contrib.admin',
|
||||||
|
'django.contrib.auth',
|
||||||
|
'django.contrib.contenttypes',
|
||||||
|
'django.contrib.sessions',
|
||||||
|
'django.contrib.messages',
|
||||||
|
'django.contrib.staticfiles',
|
||||||
|
]
|
||||||
|
|
||||||
|
MIDDLEWARE = [
|
||||||
|
'django.middleware.security.SecurityMiddleware',
|
||||||
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
|
'django.middleware.common.CommonMiddleware',
|
||||||
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
|
]
|
||||||
|
|
||||||
|
ROOT_URLCONF = 'bloomhunt.urls'
|
||||||
|
|
||||||
|
TEMPLATES = [
|
||||||
|
{
|
||||||
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
|
'DIRS': [],
|
||||||
|
'APP_DIRS': True,
|
||||||
|
'OPTIONS': {
|
||||||
|
'context_processors': [
|
||||||
|
'django.template.context_processors.debug',
|
||||||
|
'django.template.context_processors.request',
|
||||||
|
'django.contrib.auth.context_processors.auth',
|
||||||
|
'django.contrib.messages.context_processors.messages',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
WSGI_APPLICATION = 'bloomhunt.wsgi.application'
|
||||||
|
|
||||||
|
|
||||||
|
# Database
|
||||||
|
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases
|
||||||
|
|
||||||
|
DATABASES = {
|
||||||
|
'default': {
|
||||||
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
|
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Password validation
|
||||||
|
# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators
|
||||||
|
|
||||||
|
AUTH_PASSWORD_VALIDATORS = [
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# Internationalization
|
||||||
|
# https://docs.djangoproject.com/en/1.11/topics/i18n/
|
||||||
|
|
||||||
|
LANGUAGE_CODE = 'en-us'
|
||||||
|
|
||||||
|
TIME_ZONE = 'UTC'
|
||||||
|
|
||||||
|
USE_I18N = True
|
||||||
|
|
||||||
|
USE_L10N = True
|
||||||
|
|
||||||
|
USE_TZ = True
|
||||||
|
|
||||||
|
|
||||||
|
# Static files (CSS, JavaScript, Images)
|
||||||
|
# https://docs.djangoproject.com/en/1.11/howto/static-files/
|
||||||
|
|
||||||
|
STATIC_URL = '/static/'
|
||||||
|
|
||||||
|
STATICFILES_DIRS = [
|
||||||
|
os.path.join(BASE_DIR, "static"),
|
||||||
|
]
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
"""bloomhunt URL Configuration
|
||||||
|
|
||||||
|
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||||
|
https://docs.djangoproject.com/en/1.11/topics/http/urls/
|
||||||
|
Examples:
|
||||||
|
Function views
|
||||||
|
1. Add an import: from my_app import views
|
||||||
|
2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
|
||||||
|
Class-based views
|
||||||
|
1. Add an import: from other_app.views import Home
|
||||||
|
2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
|
||||||
|
Including another URLconf
|
||||||
|
1. Import the include() function: from django.conf.urls import url, include
|
||||||
|
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
|
||||||
|
"""
|
||||||
|
from django.conf.urls import url
|
||||||
|
from django.contrib import admin
|
||||||
|
from django.urls import include, path
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
url(r'^admin/', admin.site.urls),
|
||||||
|
url(r'^mobile/', include('mobile.urls')),
|
||||||
|
url(r'^map/', include('map.urls'))
|
||||||
|
]
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
"""
|
||||||
|
WSGI config for bloomhunt project.
|
||||||
|
|
||||||
|
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||||
|
|
||||||
|
For more information on this file, see
|
||||||
|
https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from django.core.wsgi import get_wsgi_application
|
||||||
|
|
||||||
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "bloomhunt.settings")
|
||||||
|
|
||||||
|
application = get_wsgi_application()
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "bloomhunt.settings")
|
||||||
|
try:
|
||||||
|
from django.core.management import execute_from_command_line
|
||||||
|
except ImportError:
|
||||||
|
# The above import may fail for some other reason. Ensure that the
|
||||||
|
# issue is really that Django is missing to avoid masking other
|
||||||
|
# exceptions on Python 2.
|
||||||
|
try:
|
||||||
|
import django
|
||||||
|
except ImportError:
|
||||||
|
raise ImportError(
|
||||||
|
"Couldn't import Django. Are you sure it's installed and "
|
||||||
|
"available on your PYTHONPATH environment variable? Did you "
|
||||||
|
"forget to activate a virtual environment?"
|
||||||
|
)
|
||||||
|
raise
|
||||||
|
execute_from_command_line(sys.argv)
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class MapConfig(AppConfig):
|
||||||
|
name = 'map'
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.db import models
|
||||||
|
|
||||||
|
# Create your models here.
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
#map1 {
|
||||||
|
height: 43%
|
||||||
|
}
|
||||||
|
|
||||||
|
#map2 {
|
||||||
|
margin-top: 10px;
|
||||||
|
height: 43%;
|
||||||
|
margin-bottom: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
width: 100%;
|
||||||
|
height: 15px;
|
||||||
|
border-radius: 5px;
|
||||||
|
background: #d3d3d3;
|
||||||
|
outline: none;
|
||||||
|
opacity: 0.7;
|
||||||
|
-webkit-transition: .2s;
|
||||||
|
transition: opacity .2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider::-webkit-slider-thumb {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
width: 25px;
|
||||||
|
height: 25px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #4CAF50;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider::-moz-range-thumb {
|
||||||
|
width: 25px;
|
||||||
|
height: 25px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #4CAF50;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-bloom-icon {
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
var p_date = document.getElementById("p_date");
|
||||||
|
|
||||||
|
var map1 = L.map('map1').setView([49.4093524, 8.6931736], 15);
|
||||||
|
var map2 = L.map('map2').setView([49.4093524, 8.6931736], 15);
|
||||||
|
|
||||||
|
map1.sync(map2);
|
||||||
|
map2.sync(map1);
|
||||||
|
|
||||||
|
var tile = "https://c.tile.openstreetmap.org/{z}/{x}/{y}.png "
|
||||||
|
|
||||||
|
var layer1 = L.tileLayer(tile, {
|
||||||
|
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||||
|
}).addTo(map1);
|
||||||
|
|
||||||
|
var layer2 = L.tileLayer(tile, {
|
||||||
|
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||||
|
}).addTo(map2);
|
||||||
|
|
||||||
|
//var layer1 = L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={accessToken}', {
|
||||||
|
// attribution: 'Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
|
||||||
|
// maxZoom: 18,
|
||||||
|
// id: 'mapbox.streets',
|
||||||
|
// accessToken: 'pk.eyJ1IjoicG9zdGZsYXYiLCJhIjoiY2syN29xZHVpMnlzcDNtbXZkN2tlYWdhaSJ9.Th5tmsyOlPKoogGrOQrMNA'
|
||||||
|
//});
|
||||||
|
|
||||||
|
//var layer2 = L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={accessToken}', {
|
||||||
|
// attribution: 'Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
|
||||||
|
// maxZoom: 18,
|
||||||
|
// id: 'mapbox.hiking',
|
||||||
|
// accessToken: 'pk.eyJ1IjoicG9zdGZsYXYiLCJhIjoiY2syN29xZHVpMnlzcDNtbXZkN2tlYWdhaSJ9.Th5tmsyOlPKoogGrOQrMNA'
|
||||||
|
//});
|
||||||
|
|
||||||
|
layer1.addTo(map1);
|
||||||
|
layer2.addTo(map2);
|
||||||
|
|
||||||
|
function mk_not_bloom_icon(label) {
|
||||||
|
return new L.DivIcon({
|
||||||
|
className: 'no-bloom-icon',
|
||||||
|
html: '<img class="no-bloom-icon" src="/home/christian/repos/bloomMap2k19/bloomhunt/map/static/map/tree_not_blooming.png"/>' +
|
||||||
|
'<span>' + label + '</span>',
|
||||||
|
iconAnchor: [15, 30]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function mk_bloom_icon(label) {
|
||||||
|
return new L.DivIcon({
|
||||||
|
className: 'bloom-icon',
|
||||||
|
html: '<img class="no-bloom-icon" src="/home/christian/repos/bloomMap2k19/bloomhunt/map/static/map/tree_blooming.png"/>' +
|
||||||
|
'<span>' + label + '</span>',
|
||||||
|
iconAnchor: [15, 30]
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//var not_bloom_icon = L.icon({
|
||||||
|
// iconUrl: 'tree_not_blooming.png',
|
||||||
|
//
|
||||||
|
// iconSize: [40, 40], // size of the icon
|
||||||
|
// shadowSize: [0, 0], // size of the shadow
|
||||||
|
// iconAnchor: [20, 40], // point of the icon which will correspond to marker's location
|
||||||
|
// shadowAnchor: [4, 62], // the same for the shadow
|
||||||
|
// popupAnchor: [-3, -76] // point from which the popup should open relative to the iconAnchor
|
||||||
|
//});
|
||||||
|
|
||||||
|
//var bloom_icon = L.icon({
|
||||||
|
// iconUrl: 'tree_blooming.png',
|
||||||
|
//
|
||||||
|
// iconSize: [40, 40], // size of the icon
|
||||||
|
// shadowSize: [0, 0], // size of the shadow
|
||||||
|
// iconAnchor: [20, 40], // point of the icon which will correspond to marker's location
|
||||||
|
// shadowAnchor: [4, 62], // the same for the shadow
|
||||||
|
// popupAnchor: [-3, -76] // point from which the popup should open relative to the iconAnchor
|
||||||
|
//});
|
||||||
|
|
||||||
|
|
||||||
|
class Tree {
|
||||||
|
constructor(name, bloom_start, xcoord, ycoord) {
|
||||||
|
this.name = name;
|
||||||
|
this.bloom_start = bloom_start;
|
||||||
|
this.marker = L.marker([xcoord, ycoord],
|
||||||
|
{icon: mk_not_bloom_icon(this.name)});
|
||||||
|
this.blooming = false;
|
||||||
|
}
|
||||||
|
update(dayno) {
|
||||||
|
if ((dayno < this.bloom_start || dayno >= this.bloom_start + 14)
|
||||||
|
&& this.blooming) {
|
||||||
|
this.marker.setIcon(mk_not_bloom_icon(this.name));
|
||||||
|
this.blooming = false;
|
||||||
|
} else if (dayno >= this.bloom_start
|
||||||
|
&& dayno < this.bloom_start + 14
|
||||||
|
&& !this.blooming) {
|
||||||
|
this.marker.setIcon(mk_bloom_icon(this.name));
|
||||||
|
this.blooming = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var tree = new Tree("Fritz", 55, 49.4093524, 8.6931736);
|
||||||
|
var tree2 = new Tree("Hans", 80, 45.02, 8.6931736);
|
||||||
|
var tree3 = new Tree("Hans", 80, 49.02, 8.69317);
|
||||||
|
|
||||||
|
var a_tree = new Tree("Fritz", 120, 49.4093524, 8.6931736);
|
||||||
|
var a_tree2 = new Tree("Hans", 100, 45.02, 8.6931736);
|
||||||
|
var a_tree3 = new Tree("Hans", 100, 49.02, 8.69317);
|
||||||
|
|
||||||
|
function updateMap(value) {
|
||||||
|
tree.update(value);
|
||||||
|
tree2.update(value);
|
||||||
|
tree3.update(value);
|
||||||
|
a_tree.update(value);
|
||||||
|
a_tree2.update(value);
|
||||||
|
a_tree3.update(value);
|
||||||
|
p_date.innerHTML = "Date: " + dateFromDay(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function dateFromDay(dayno) {
|
||||||
|
var date = new Date(2019, 0); // initialize a date in `year-01-01`
|
||||||
|
var date2 = new Date(date.setDate(dayno));
|
||||||
|
return date2.getMonth() + 1 + "-" + date2.getDate();
|
||||||
|
}
|
||||||
|
|
||||||
|
var cities1 = L.layerGroup([tree.marker, tree2.marker, tree3.marker]);
|
||||||
|
var cities2 = L.layerGroup([a_tree.marker, a_tree2.marker, a_tree3.marker]);
|
||||||
|
cities1.addTo(map1);
|
||||||
|
cities2.addTo(map2);
|
||||||
|
|
||||||
|
//var control1 = L.control.layers({'2019': cities1, '2020': cities1}, null, {collapsed: false});
|
||||||
|
//var control2 = L.control.layers({'2019': cities2, '2020': cities2}, null, {collapsed: false});
|
||||||
|
|
||||||
|
//control1.addTo(map1);
|
||||||
|
//control2.addTo(map2);
|
||||||
|
|
||||||
|
//map1.on('baselayerchange', function(val) {
|
||||||
|
// console.log(val);
|
||||||
|
//});
|
||||||
|
//
|
||||||
|
//map2.on('baselayerchange', function(val) {
|
||||||
|
// console.log("map2", val);
|
||||||
|
//});
|
||||||
@@ -0,0 +1,269 @@
|
|||||||
|
/*
|
||||||
|
* Extends L.Map to synchronize the interaction on one map to one or more other maps.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
var NO_ANIMATION = {
|
||||||
|
animate: false,
|
||||||
|
reset: true,
|
||||||
|
disableViewprereset: true
|
||||||
|
};
|
||||||
|
|
||||||
|
L.Sync = function () {};
|
||||||
|
/*
|
||||||
|
* Helper function to compute the offset easily.
|
||||||
|
*
|
||||||
|
* The arguments are relative positions with respect to reference and target maps of
|
||||||
|
* the point to sync. If you provide ratioRef=[0, 1], ratioTarget=[1, 0] will sync the
|
||||||
|
* bottom left corner of the reference map with the top right corner of the target map.
|
||||||
|
* The values can be less than 0 or greater than 1. It will sync points out of the map.
|
||||||
|
*/
|
||||||
|
L.Sync.offsetHelper = function (ratioRef, ratioTarget) {
|
||||||
|
var or = L.Util.isArray(ratioRef) ? ratioRef : [0.5, 0.5];
|
||||||
|
var ot = L.Util.isArray(ratioTarget) ? ratioTarget : [0.5, 0.5];
|
||||||
|
return function (center, zoom, refMap, targetMap) {
|
||||||
|
var rs = refMap.getSize();
|
||||||
|
var ts = targetMap.getSize();
|
||||||
|
var pt = refMap.project(center, zoom)
|
||||||
|
.subtract([(0.5 - or[0]) * rs.x, (0.5 - or[1]) * rs.y])
|
||||||
|
.add([(0.5 - ot[0]) * ts.x, (0.5 - ot[1]) * ts.y]);
|
||||||
|
return refMap.unproject(pt, zoom);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
L.Map.include({
|
||||||
|
sync: function (map, options) {
|
||||||
|
this._initSync();
|
||||||
|
options = L.extend({
|
||||||
|
noInitialSync: false,
|
||||||
|
syncCursor: false,
|
||||||
|
syncCursorMarkerOptions: {
|
||||||
|
radius: 10,
|
||||||
|
fillOpacity: 0.3,
|
||||||
|
color: '#da291c',
|
||||||
|
fillColor: '#fff'
|
||||||
|
},
|
||||||
|
offsetFn: function (center, zoom, refMap, targetMap) {
|
||||||
|
// no transformation at all
|
||||||
|
return center;
|
||||||
|
}
|
||||||
|
}, options);
|
||||||
|
|
||||||
|
// prevent double-syncing the map:
|
||||||
|
if (this._syncMaps.indexOf(map) === -1) {
|
||||||
|
this._syncMaps.push(map);
|
||||||
|
this._syncOffsetFns[L.Util.stamp(map)] = options.offsetFn;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.noInitialSync) {
|
||||||
|
map.setView(
|
||||||
|
options.offsetFn(this.getCenter(), this.getZoom(), this, map),
|
||||||
|
this.getZoom(), NO_ANIMATION);
|
||||||
|
}
|
||||||
|
if (options.syncCursor) {
|
||||||
|
if (typeof map.cursor === 'undefined') {
|
||||||
|
map.cursor = L.circleMarker([0, 0], options.syncCursorMarkerOptions).addTo(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._cursors.push(map.cursor);
|
||||||
|
|
||||||
|
this.on('mousemove', this._cursorSyncMove, this);
|
||||||
|
this.on('mouseout', this._cursorSyncOut, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// on these events, we should reset the view on every synced map
|
||||||
|
// dragstart is due to inertia
|
||||||
|
this.on('resize zoomend', this._selfSetView);
|
||||||
|
this.on('moveend', this._syncOnMoveend);
|
||||||
|
this.on('dragend', this._syncOnDragend);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
// unsync maps from each other
|
||||||
|
unsync: function (map) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
if (this._cursors) {
|
||||||
|
this._cursors.forEach(function (cursor, indx, _cursors) {
|
||||||
|
if (cursor === map.cursor) {
|
||||||
|
_cursors.splice(indx, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: hide cursor in stead of moving to 0, 0
|
||||||
|
if (map.cursor) {
|
||||||
|
map.cursor.setLatLng([0, 0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._syncMaps) {
|
||||||
|
this._syncMaps.forEach(function (synced, id) {
|
||||||
|
if (map === synced) {
|
||||||
|
delete self._syncOffsetFns[L.Util.stamp(map)];
|
||||||
|
self._syncMaps.splice(id, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._syncMaps || this._syncMaps.length == 0) {
|
||||||
|
// no more synced maps, so these events are not needed.
|
||||||
|
this.off('resize zoomend', this._selfSetView);
|
||||||
|
this.off('moveend', this._syncOnMoveend);
|
||||||
|
this.off('dragend', this._syncOnDragend);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Checks if the map is synced with anything or a specifyc map
|
||||||
|
isSynced: function (otherMap) {
|
||||||
|
var has = (this.hasOwnProperty('_syncMaps') && Object.keys(this._syncMaps).length > 0);
|
||||||
|
if (has && otherMap) {
|
||||||
|
// Look for this specific map
|
||||||
|
has = false;
|
||||||
|
this._syncMaps.forEach(function (synced) {
|
||||||
|
if (otherMap == synced) { has = true; }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return has;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
// Callbacks for events...
|
||||||
|
_cursorSyncMove: function (e) {
|
||||||
|
this._cursors.forEach(function (cursor) {
|
||||||
|
cursor.setLatLng(e.latlng);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_cursorSyncOut: function (e) {
|
||||||
|
this._cursors.forEach(function (cursor) {
|
||||||
|
// TODO: hide cursor in stead of moving to 0, 0
|
||||||
|
cursor.setLatLng([0, 0]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_selfSetView: function (e) {
|
||||||
|
// reset the map, and let setView synchronize the others.
|
||||||
|
this.setView(this.getCenter(), this.getZoom(), NO_ANIMATION);
|
||||||
|
},
|
||||||
|
|
||||||
|
_syncOnMoveend: function (e) {
|
||||||
|
if (this._syncDragend) {
|
||||||
|
// This is 'the moveend' after the dragend.
|
||||||
|
// Without inertia, it will be right after,
|
||||||
|
// but when inertia is on, we need this to detect that.
|
||||||
|
this._syncDragend = false; // before calling setView!
|
||||||
|
this._selfSetView(e);
|
||||||
|
this._syncMaps.forEach(function (toSync) {
|
||||||
|
toSync.fire('moveend');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_syncOnDragend: function (e) {
|
||||||
|
// It is ugly to have state, but we need it in case of inertia.
|
||||||
|
this._syncDragend = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
// overload methods on originalMap to replay interactions on _syncMaps;
|
||||||
|
_initSync: function () {
|
||||||
|
if (this._syncMaps) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var originalMap = this;
|
||||||
|
|
||||||
|
this._syncMaps = [];
|
||||||
|
this._cursors = [];
|
||||||
|
this._syncOffsetFns = {};
|
||||||
|
|
||||||
|
L.extend(originalMap, {
|
||||||
|
setView: function (center, zoom, options, sync) {
|
||||||
|
// Use this sandwich to disable and enable viewprereset
|
||||||
|
// around setView call
|
||||||
|
function sandwich (obj, fn) {
|
||||||
|
var viewpreresets = [];
|
||||||
|
var doit = options && options.disableViewprereset && obj && obj._events;
|
||||||
|
if (doit) {
|
||||||
|
// The event viewpreresets does an invalidateAll,
|
||||||
|
// that reloads all the tiles.
|
||||||
|
// That causes an annoying flicker.
|
||||||
|
viewpreresets = obj._events.viewprereset;
|
||||||
|
obj._events.viewprereset = [];
|
||||||
|
}
|
||||||
|
var ret = fn(obj);
|
||||||
|
if (doit) {
|
||||||
|
// restore viewpreresets event to its previous values
|
||||||
|
obj._events.viewprereset = viewpreresets;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looks better if the other maps 'follow' the active one,
|
||||||
|
// so call this before _syncMaps
|
||||||
|
var ret = sandwich(this, function (obj) {
|
||||||
|
return L.Map.prototype.setView.call(obj, center, zoom, options);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!sync) {
|
||||||
|
originalMap._syncMaps.forEach(function (toSync) {
|
||||||
|
sandwich(toSync, function (obj) {
|
||||||
|
return toSync.setView(
|
||||||
|
originalMap._syncOffsetFns[L.Util.stamp(toSync)](center, zoom, originalMap, toSync),
|
||||||
|
zoom, options, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
},
|
||||||
|
|
||||||
|
panBy: function (offset, options, sync) {
|
||||||
|
if (!sync) {
|
||||||
|
originalMap._syncMaps.forEach(function (toSync) {
|
||||||
|
toSync.panBy(offset, options, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return L.Map.prototype.panBy.call(this, offset, options);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onResize: function (event, sync) {
|
||||||
|
if (!sync) {
|
||||||
|
originalMap._syncMaps.forEach(function (toSync) {
|
||||||
|
toSync._onResize(event, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return L.Map.prototype._onResize.call(this, event);
|
||||||
|
},
|
||||||
|
|
||||||
|
_stop: function (sync) {
|
||||||
|
L.Map.prototype._stop.call(this);
|
||||||
|
if (!sync) {
|
||||||
|
originalMap._syncMaps.forEach(function (toSync) {
|
||||||
|
toSync._stop(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
originalMap.dragging._draggable._updatePosition = function () {
|
||||||
|
L.Draggable.prototype._updatePosition.call(this);
|
||||||
|
var self = this;
|
||||||
|
originalMap._syncMaps.forEach(function (toSync) {
|
||||||
|
L.DomUtil.setPosition(toSync.dragging._draggable._element, self._newPos);
|
||||||
|
toSync.eachLayer(function (layer) {
|
||||||
|
if (layer._google !== undefined) {
|
||||||
|
var offsetFn = originalMap._syncOffsetFns[L.Util.stamp(toSync)];
|
||||||
|
var center = offsetFn(originalMap.getCenter(), originalMap.getZoom(), originalMap, toSync);
|
||||||
|
layer._google.setCenter(center);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
toSync.fire('move');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 47 KiB |
@@ -0,0 +1,177 @@
|
|||||||
|
{% load static %}
|
||||||
|
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>Bloommap</title>
|
||||||
|
|
||||||
|
<link rel="shortcut icon" type="image/png" href="{% static 'favicon.ico' %}"/>
|
||||||
|
<link rel="stylesheet" href="{% static 'map/index.css' %}">
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.5.1/dist/leaflet.css"
|
||||||
|
integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ=="
|
||||||
|
crossorigin=""/>
|
||||||
|
<script src="https://unpkg.com/leaflet@1.5.1/dist/leaflet.js"
|
||||||
|
integrity="sha512-GffPMF3RvMeYyc1LWMHtK8EbPv0iNZ8/oTtHPx9/cc2ILxQ+u905qIwdpULaqDkyBKgOaB57QTMg7ztg8Jm2Og=="
|
||||||
|
crossorigin=""></script>
|
||||||
|
<script src="{% static 'map/mapsync.js' %}"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="map1"></div>
|
||||||
|
<div id="map2"></div>
|
||||||
|
|
||||||
|
<div style="pointer-events: none; position: absolute; top: 30px; right: 40px; z-index: 1000">2019</div>
|
||||||
|
<div style="pointer-events: none; position: absolute; top: 310px; right: 40px; z-index: 1001">2029</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="slidecontainer">
|
||||||
|
<input type="range" min="1" max="365" value="180" class="slider" id="myRange"
|
||||||
|
oninput="updateMap(this.value)">
|
||||||
|
</div>
|
||||||
|
<p id="p_date"></p>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
var p_date = document.getElementById("p_date");
|
||||||
|
|
||||||
|
var map1 = L.map('map1').setView([49.4093524, 8.6931736], 15);
|
||||||
|
var map2 = L.map('map2').setView([49.4093524, 8.6931736], 15);
|
||||||
|
|
||||||
|
map1.sync(map2);
|
||||||
|
map2.sync(map1);
|
||||||
|
|
||||||
|
var tile = "https://c.tile.openstreetmap.org/{z}/{x}/{y}.png "
|
||||||
|
|
||||||
|
//var layer1 = L.tileLayer(tile, {
|
||||||
|
// attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||||
|
//}).addTo(map1);
|
||||||
|
//
|
||||||
|
//var layer2 = L.tileLayer(tile, {
|
||||||
|
// attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||||
|
//}).addTo(map2);
|
||||||
|
|
||||||
|
var layer1 = L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={accessToken}', {
|
||||||
|
attribution: 'Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
|
||||||
|
maxZoom: 18,
|
||||||
|
id: 'mapbox.streets',
|
||||||
|
accessToken: 'pk.eyJ1IjoicG9zdGZsYXYiLCJhIjoiY2syN29xZHVpMnlzcDNtbXZkN2tlYWdhaSJ9.Th5tmsyOlPKoogGrOQrMNA'
|
||||||
|
});
|
||||||
|
|
||||||
|
var layer2 = L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={accessToken}', {
|
||||||
|
attribution: 'Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
|
||||||
|
maxZoom: 18,
|
||||||
|
id: 'mapbox.streets',
|
||||||
|
accessToken: 'pk.eyJ1IjoicG9zdGZsYXYiLCJhIjoiY2syN29xZHVpMnlzcDNtbXZkN2tlYWdhaSJ9.Th5tmsyOlPKoogGrOQrMNA'
|
||||||
|
});
|
||||||
|
|
||||||
|
layer1.addTo(map1);
|
||||||
|
layer2.addTo(map2);
|
||||||
|
|
||||||
|
function mk_not_bloom_icon(label) {
|
||||||
|
return new L.DivIcon({
|
||||||
|
className: 'no-bloom-icon',
|
||||||
|
html: '<img class="no-bloom-icon" src="{% static 'map/tree_not_blooming.png' %}"/>' +
|
||||||
|
'<span>' + label + '</span>',
|
||||||
|
iconAnchor: [15, 30]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function mk_bloom_icon(label) {
|
||||||
|
return new L.DivIcon({
|
||||||
|
className: 'bloom-icon',
|
||||||
|
html: '<img class="no-bloom-icon" src="{% static 'map/tree_blooming.png' %}"/>' +
|
||||||
|
'<span>' + label + '</span>',
|
||||||
|
iconAnchor: [15, 30]
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//var not_bloom_icon = L.icon({
|
||||||
|
// iconUrl: 'tree_not_blooming.png',
|
||||||
|
//
|
||||||
|
// iconSize: [40, 40], // size of the icon
|
||||||
|
// shadowSize: [0, 0], // size of the shadow
|
||||||
|
// iconAnchor: [20, 40], // point of the icon which will correspond to marker's location
|
||||||
|
// shadowAnchor: [4, 62], // the same for the shadow
|
||||||
|
// popupAnchor: [-3, -76] // point from which the popup should open relative to the iconAnchor
|
||||||
|
//});
|
||||||
|
|
||||||
|
//var bloom_icon = L.icon({
|
||||||
|
// iconUrl: 'tree_blooming.png',
|
||||||
|
//
|
||||||
|
// iconSize: [40, 40], // size of the icon
|
||||||
|
// shadowSize: [0, 0], // size of the shadow
|
||||||
|
// iconAnchor: [20, 40], // point of the icon which will correspond to marker's location
|
||||||
|
// shadowAnchor: [4, 62], // the same for the shadow
|
||||||
|
// popupAnchor: [-3, -76] // point from which the popup should open relative to the iconAnchor
|
||||||
|
//});
|
||||||
|
|
||||||
|
|
||||||
|
class Tree {
|
||||||
|
constructor(name, bloom_start, xcoord, ycoord) {
|
||||||
|
this.name = name;
|
||||||
|
this.bloom_start = bloom_start;
|
||||||
|
this.marker = L.marker([xcoord, ycoord],
|
||||||
|
{icon: mk_not_bloom_icon(this.name)});
|
||||||
|
this.blooming = false;
|
||||||
|
}
|
||||||
|
update(dayno) {
|
||||||
|
if ((dayno < this.bloom_start || dayno >= this.bloom_start + 14)
|
||||||
|
&& this.blooming) {
|
||||||
|
this.marker.setIcon(mk_not_bloom_icon(this.name));
|
||||||
|
this.blooming = false;
|
||||||
|
} else if (dayno >= this.bloom_start
|
||||||
|
&& dayno < this.bloom_start + 14
|
||||||
|
&& !this.blooming) {
|
||||||
|
this.marker.setIcon(mk_bloom_icon(this.name));
|
||||||
|
this.blooming = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var treesFromJSON = {{ trees|safe }}["trees"];
|
||||||
|
var trees_1 = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < treesFromJSON.length; i++) {
|
||||||
|
let raw = treesFromJSON[i];
|
||||||
|
let tree = new Tree(raw["name"], raw["bloom_start"],
|
||||||
|
raw["latitude"], raw["longitude"]);
|
||||||
|
trees_1.push(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
var trees_2 = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < treesFromJSON.length; i++) {
|
||||||
|
let raw = treesFromJSON[i];
|
||||||
|
let tree = new Tree(raw["name"], raw["bloom_start"] + 20,
|
||||||
|
raw["latitude"], raw["longitude"]);
|
||||||
|
trees_2.push(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateMap(value) {
|
||||||
|
for (var i = 0; i < trees_1.length; i++) {
|
||||||
|
trees_1[i].update(value);
|
||||||
|
}
|
||||||
|
for (var i = 0; i < trees_2.length; i++) {
|
||||||
|
trees_2[i].update(value);
|
||||||
|
}
|
||||||
|
p_date.innerHTML = "Date: " + dateFromDay(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function dateFromDay(dayno) {
|
||||||
|
var date = new Date(2019, 0); // initialize a date in `year-01-01`
|
||||||
|
var date2 = new Date(date.setDate(dayno));
|
||||||
|
return date2.getMonth() + 1 + "-" + date2.getDate();
|
||||||
|
}
|
||||||
|
|
||||||
|
var cities1 = L.layerGroup(trees_1.map(t => t.marker));
|
||||||
|
var cities2 = L.layerGroup(trees_2.map(t => t.marker));
|
||||||
|
cities1.addTo(map1);
|
||||||
|
cities2.addTo(map2);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('', views.index, name='index')
|
||||||
|
]
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
from django.http import HttpResponse
|
||||||
|
from django.shortcuts import render
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
def index(request):
|
||||||
|
if not os.path.isfile("/tmp/data.json"):
|
||||||
|
data = {"trees": str({ "trees": []}) }
|
||||||
|
else:
|
||||||
|
with open("/tmp/data.json", "r") as f:
|
||||||
|
loaded = json.load(f)
|
||||||
|
s = json.dumps(loaded);
|
||||||
|
data = {"trees": s}
|
||||||
|
return render(request, "map/index.html", context=data);
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class MobileConfig(AppConfig):
|
||||||
|
name = 'mobile'
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.db import models
|
||||||
|
|
||||||
|
# Create your models here.
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id=content>
|
||||||
|
{% block content %}
|
||||||
|
{% endblock %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{% extends "mobile/base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<body>
|
||||||
|
<h1>Bloomhunt</h1>
|
||||||
|
|
||||||
|
<p>Hunt blooming trees in your area.</p>
|
||||||
|
|
||||||
|
<a class="btn btn-default" id="btn_scan" href="/mobile/scan">Scan a blooming tree</a>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{% extends "mobile/base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>Scan a blooming tree!</h1>
|
||||||
|
|
||||||
|
<p>We will help you identifying the tree you are hunting.</p>
|
||||||
|
|
||||||
|
<p>{{ question.text }}</p>
|
||||||
|
|
||||||
|
{% for option in question.options %}
|
||||||
|
|
||||||
|
<form action="" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input name="question" type="hidden" value="{{ question.key }}">
|
||||||
|
<input name="chosen_option" class="btn btn-default" type="submit" value="{{ option.text }}">
|
||||||
|
</form>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
{% extends "mobile/base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<p>Congratulions, you are the first to report that this <b>{{ tree.name }}</b>
|
||||||
|
tree is blooming.<p>
|
||||||
|
|
||||||
|
<p>You may give it a name now</p>
|
||||||
|
|
||||||
|
<form action="/mobile/addtree/" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="text" name="name">
|
||||||
|
<input type="hidden" name="tree" value="{{ tree.name }}">
|
||||||
|
<input id="in_long" type="text" name="longitude">
|
||||||
|
<input id="in_lat" type="text" name="latitude">
|
||||||
|
<input type="submit" value="Name your tree">
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
navigator.geolocation.getCurrentPosition(function(position) {
|
||||||
|
var in_long = document.getElementById("in_long");
|
||||||
|
var in_lat = document.getElementById("in_lat");
|
||||||
|
in_long.value = position.coords.longitude;
|
||||||
|
in_lat.value = position.coords.latitude;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{% extends "mobile/base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<h1>You are now one of the glorious Bloom Hunters!</h1>
|
||||||
|
|
||||||
|
<p>Does that feel great? It should! You really achieved something in
|
||||||
|
your life!</p>
|
||||||
|
|
||||||
|
<a class="btn btn-default" href="/mobile">Back to hunting</a>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('addtree/', views.addtree, name='addtree'),
|
||||||
|
path('scan/', views.scan , name='scan'),
|
||||||
|
path('', views.index, name='index')
|
||||||
|
]
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
from django.http import HttpResponse, HttpResponseRedirect
|
||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
def index(request):
|
||||||
|
return render(request, 'mobile/index.html')
|
||||||
|
|
||||||
|
|
||||||
|
def scan(request):
|
||||||
|
question = q1
|
||||||
|
if request.POST:
|
||||||
|
data = request.POST
|
||||||
|
print("data")
|
||||||
|
if data["question"]:
|
||||||
|
print("response to question", data["question"])
|
||||||
|
for q in questions:
|
||||||
|
if not q.key == data["question"]:
|
||||||
|
continue
|
||||||
|
for opt in q.options:
|
||||||
|
if not opt.text == data["chosen_option"]:
|
||||||
|
continue
|
||||||
|
if opt.question:
|
||||||
|
question = opt.question
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
return scanned(request, opt.result)
|
||||||
|
# first question
|
||||||
|
context = {'question': question}
|
||||||
|
return render(request, 'mobile/scan.html', context)
|
||||||
|
|
||||||
|
|
||||||
|
def scanned(request, tree):
|
||||||
|
context = {'tree': {'name': tree}}
|
||||||
|
return render(request, 'mobile/scanned.html', context)
|
||||||
|
|
||||||
|
|
||||||
|
def addtree(request):
|
||||||
|
if not request.POST:
|
||||||
|
return HttpResponseRedirect("/mobile/scan")
|
||||||
|
name = request.POST["name"]
|
||||||
|
tree = request.POST["name"]
|
||||||
|
longitude = request.POST["longitude"]
|
||||||
|
latitude = request.POST["latitude"]
|
||||||
|
bloom_start = datetime.now().timetuple().tm_yday
|
||||||
|
if not os.path.isfile('/tmp/data.json'):
|
||||||
|
data = {'trees': []}
|
||||||
|
else:
|
||||||
|
with open('/tmp/data.json', 'r') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
with open('/tmp/data.json', 'w') as f:
|
||||||
|
data["trees"].append(
|
||||||
|
{'name': name, 'species': tree,
|
||||||
|
'longitude': longitude, 'latitude': latitude,
|
||||||
|
'bloom_start': bloom_start }
|
||||||
|
)
|
||||||
|
json.dump(data, f, indent=4)
|
||||||
|
return render(request, "mobile/thankyou.html")
|
||||||
|
|
||||||
|
|
||||||
|
class Option:
|
||||||
|
def __init__(self, text, question=None, result=None):
|
||||||
|
self.text = text
|
||||||
|
self.question = question
|
||||||
|
self.result = result
|
||||||
|
|
||||||
|
|
||||||
|
class Question:
|
||||||
|
def __init__(self, key, text, options):
|
||||||
|
self.key = key
|
||||||
|
self.text = text
|
||||||
|
self.options = options
|
||||||
|
|
||||||
|
|
||||||
|
q3 = Question("alive", "Is your tree alive?",
|
||||||
|
[Option("yes", result="Poor Apple Tree"),
|
||||||
|
Option("no", result="Dead Apple Tree")])
|
||||||
|
q2 = Question("red", "Is your tree red?",
|
||||||
|
[Option("yes", result="Red Apple Tree"),
|
||||||
|
Option("no", result="Green Apple Tree")])
|
||||||
|
q1 = Question("leaves", "Does your tree have leaves?",
|
||||||
|
[Option("yes", question=q2), Option("no", question=q3)])
|
||||||
|
|
||||||
|
questions = [q1, q2, q3]
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 47 KiB |
Reference in New Issue
Block a user