├── requirements.txt
├── www
├── static
│ ├── images
│ │ ├── toggle.png
│ │ ├── marker-red.png
│ │ ├── marker-green.png
│ │ ├── marker-shadow.png
│ │ └── toggle.svg
│ ├── Control.MiniMap.min.css
│ ├── profile.js
│ ├── PermalinkAttribution.js
│ ├── style.css
│ ├── leaflet-hash.js
│ ├── Bing.js
│ ├── StreetViewButtons.js
│ ├── Control.MiniMap.min.js
│ └── audit.js
├── __init__.py
├── templates
│ ├── admin.html
│ ├── layout.html
│ ├── index.html
│ ├── profile.html
│ ├── newproject.html
│ ├── map.html
│ ├── project.html
│ ├── browse.html
│ ├── table.html
│ └── task.html
├── util.py
├── db.py
└── audit.py
├── .gitignore
├── cf_audit.wsgi
├── run.py
├── README.md
├── config.py
├── update_features.py
└── LICENSE
/requirements.txt:
--------------------------------------------------------------------------------
1 | peewee==2.10.2
2 | flask>=0.11
3 | flask-Compress
4 | flask-OAuthlib
5 |
--------------------------------------------------------------------------------
/www/static/images/toggle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zverik/cf_audit/master/www/static/images/toggle.png
--------------------------------------------------------------------------------
/www/static/images/marker-red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zverik/cf_audit/master/www/static/images/marker-red.png
--------------------------------------------------------------------------------
/www/static/images/marker-green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zverik/cf_audit/master/www/static/images/marker-green.png
--------------------------------------------------------------------------------
/www/static/images/marker-shadow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zverik/cf_audit/master/www/static/images/marker-shadow.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__/
2 | mockup/
3 | venv/
4 | config_local.py
5 | *.pyc
6 | *.db
7 | *.swp
8 | *.log
9 | *.backup
10 | *.sql
11 | *.gz
12 |
--------------------------------------------------------------------------------
/www/__init__.py:
--------------------------------------------------------------------------------
1 | from flask import Flask
2 |
3 | app = Flask(__name__)
4 | app.config.from_object('config')
5 |
6 | try:
7 | from flask_compress import Compress
8 | Compress(app)
9 | except ImportError:
10 | pass
11 |
12 | import www.audit
13 |
--------------------------------------------------------------------------------
/cf_audit.wsgi:
--------------------------------------------------------------------------------
1 | import os, sys
2 | BASE_DIR = os.path.abspath(os.path.dirname(__file__))
3 | sys.path.insert(0, BASE_DIR)
4 | PYTHON = 'python2.7'
5 | VENV_DIR = os.path.join(BASE_DIR, 'venv', 'lib', PYTHON, 'site-packages')
6 | if os.path.exists(VENV_DIR):
7 | sys.path.insert(1, VENV_DIR)
8 |
9 | from www import app as application
10 |
--------------------------------------------------------------------------------
/run.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import sys
4 | BASE_DIR = os.path.abspath(os.path.dirname(__file__))
5 | sys.path.insert(0, BASE_DIR)
6 | PYTHON = 'python2.7'
7 | VENV_DIR = os.path.join(BASE_DIR, 'venv', 'lib', PYTHON, 'site-packages')
8 | if os.path.exists(VENV_DIR):
9 | sys.path.insert(1, VENV_DIR)
10 |
11 | from www import app
12 | from www.db import migrate
13 | migrate()
14 | app.run(debug=True)
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # OSM Conflation Audit
2 |
3 | This website takes a JSON output from [OSM Conflator](https://github.com/mapsme/osm_conflate)
4 | and presents logged-in users an interface for validating each imported point, one-by-one.
5 | It records any changes and produces a file that can be later feeded back to the Conflator.
6 |
7 | ## Author and License
8 |
9 | All this was written by Ilya Zverev for MAPS.ME. Published under Apache License 2.0.
10 |
--------------------------------------------------------------------------------
/www/templates/admin.html:
--------------------------------------------------------------------------------
1 | {% extends "layout.html" %}
2 | {% block title %}Administration — {% endblock %}
3 | {% block content %}
4 |
Administration
5 |
11 | Return
12 | {% endblock %}
13 |
--------------------------------------------------------------------------------
/config.py:
--------------------------------------------------------------------------------
1 | import os
2 | BASE_DIR = os.path.abspath(os.path.dirname(__file__))
3 |
4 | DEBUG = True
5 |
6 | DATABASE_URI = 'sqlite:///' + os.path.join(BASE_DIR, 'audit.db')
7 | # DATABASE_URI = 'postgresql://localhost/cf_audit'
8 | MAX_CONTENT_LENGTH = 16*1024*1024
9 |
10 | ADMINS = set([290271]) # Zverik
11 |
12 | # Override these (and anything else) in config_local.py
13 | OAUTH_KEY = ''
14 | OAUTH_SECRET = ''
15 | SECRET_KEY = 'sdkjfhsfljhsadf'
16 | MAPILLARY_CLIENT_ID = ''
17 |
18 | try:
19 | from config_local import *
20 | except ImportError:
21 | pass
22 |
--------------------------------------------------------------------------------
/www/templates/layout.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {% block title %}{% endblock %}Conflate Audit
5 |
6 |
7 | {% block header %}{% endblock %}
8 |
12 |
13 |
14 | {% block content %}{% endblock %}
15 |
16 |
17 |
--------------------------------------------------------------------------------
/www/templates/index.html:
--------------------------------------------------------------------------------
1 | {% extends "layout.html" %}
2 | {% block content %}
3 | Auditing Tool for OSM Conflator
4 | {% if admin %}
5 | Create a project
6 | {% endif %}
7 | Imports that need validating:
8 |
9 | {% for proj in projects %}
10 | {% if not proj.hidden or is_admin(proj) %}
11 | {% if proj.hidden %}🕶️ {% endif %}{{ proj.title }} ({{ proj.feature_count }})
12 | {% endif %}
13 | {% endfor %}
14 |
15 | {% if not user %}
16 | Login to validate imports
17 | {% else %}
18 | Your settings
19 | {% endif %}
20 | {% endblock %}
21 |
--------------------------------------------------------------------------------
/www/templates/profile.html:
--------------------------------------------------------------------------------
1 | {% extends "layout.html" %}
2 | {% block title %}Profile — {% endblock %}
3 | {% block header %}
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | {% endblock %}
12 | {% block content %}
13 | User Profile
14 | Regions that you are most familiar with:
15 |
16 |
17 |
18 |
19 |
20 | Return
21 | {% endblock %}
22 |
--------------------------------------------------------------------------------
/www/static/Control.MiniMap.min.css:
--------------------------------------------------------------------------------
1 | .leaflet-control-minimap{border:rgba(255,255,255,1) solid;box-shadow:0 1px 5px rgba(0,0,0,.65);border-radius:3px;background:#f8f8f9;transition:all .6s}.leaflet-control-minimap a{background-color:rgba(255,255,255,1);background-repeat:no-repeat;z-index:99999;transition:all .6s}.leaflet-control-minimap a.minimized-bottomright{-webkit-transform:rotate(180deg);transform:rotate(180deg);border-radius:0}.leaflet-control-minimap a.minimized-topleft{-webkit-transform:rotate(0deg);transform:rotate(0deg);border-radius:0}.leaflet-control-minimap a.minimized-bottomleft{-webkit-transform:rotate(270deg);transform:rotate(270deg);border-radius:0}.leaflet-control-minimap a.minimized-topright{-webkit-transform:rotate(90deg);transform:rotate(90deg);border-radius:0}.leaflet-control-minimap-toggle-display{background-image:url(images/toggle.svg);background-size:cover;position:absolute;border-radius:3px 0 0}.leaflet-oldie .leaflet-control-minimap-toggle-display{background-image:url(images/toggle.png)}.leaflet-control-minimap-toggle-display-bottomright{bottom:0;right:0}.leaflet-control-minimap-toggle-display-topleft{top:0;left:0;-webkit-transform:rotate(180deg);transform:rotate(180deg)}.leaflet-control-minimap-toggle-display-bottomleft{bottom:0;left:0;-webkit-transform:rotate(90deg);transform:rotate(90deg)}.leaflet-control-minimap-toggle-display-topright{top:0;right:0;-webkit-transform:rotate(270deg);transform:rotate(270deg)}.leaflet-oldie .leaflet-control-minimap{border:1px solid #999}.leaflet-oldie .leaflet-control-minimap a{background-color:#fff}.leaflet-oldie .leaflet-control-minimap a.minimized{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2)}
--------------------------------------------------------------------------------
/www/static/images/toggle.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/update_features.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import sys
4 |
5 | BASE_DIR = os.path.abspath(os.path.dirname(__file__))
6 | sys.path.insert(0, BASE_DIR)
7 | PYTHON = 'python2.7'
8 | VENV_DIR = os.path.join(BASE_DIR, 'venv', 'lib', PYTHON, 'site-packages')
9 | if os.path.exists(VENV_DIR):
10 | sys.path.insert(1, VENV_DIR)
11 |
12 | import codecs
13 | import datetime
14 | import logging
15 | import json
16 | from www.db import Project, database
17 | from www.util import update_features, update_features_cache
18 |
19 | if len(sys.argv) < 3:
20 | print "Usage: {} []".format(sys.argv[0])
21 | sys.exit(1)
22 |
23 | logging.basicConfig(level=logging.INFO, format='%(asctime)s %(message)s', datefmt='%H:%M:%S')
24 | logging.info('Reading JSON files')
25 |
26 | if sys.argv[2] == '-':
27 | features = []
28 | else:
29 | with codecs.open(sys.argv[2], 'r', 'utf-8') as f:
30 | features = json.load(f)['features']
31 |
32 | audit = None
33 | if len(sys.argv) > 3:
34 | with codecs.open(sys.argv[3], 'r', 'utf-8') as f:
35 | audit = json.load(f)
36 |
37 | if not features and not audit:
38 | logging.error("No features read")
39 | sys.exit(2)
40 |
41 | try:
42 | project = Project.get(Project.name == sys.argv[1])
43 | except Project.DoesNotExist:
44 | logging.error("No such project: %s", sys.argv[1])
45 | sys.exit(2)
46 |
47 | logging.info('Updating features')
48 |
49 | proj_audit = json.loads(project.audit or '{}')
50 | if audit:
51 | proj_audit.update(audit)
52 | project.audit = json.dumps(proj_audit, ensure_ascii=False)
53 | project.updated = datetime.datetime.utcnow().date()
54 |
55 | with database.atomic():
56 | update_features(project, features, proj_audit)
57 |
58 | logging.info('Updating the feature cache')
59 | update_features_cache(project)
60 | project.save()
61 |
--------------------------------------------------------------------------------
/www/templates/newproject.html:
--------------------------------------------------------------------------------
1 | {% extends "layout.html" %}
2 | {% block title %}{% if project.name %}Edit{% else %}Create{% endif %} a project — {% endblock %}
3 | {% block content %}
4 | {% if project.name %}Update{% else %}Create{% endif %} a project
5 |
6 |
7 | Short name:
8 | Title:
9 | URL:
10 | Description:
11 | {{ project.description or '' }}
12 |
13 | JSON:
14 | Audit:
15 | Hide from the projects list
16 | Enable validation
17 | Validate only modified features
18 | Split validation by regions
19 | Show proprietary Street View
20 |
21 |
22 |
23 | {% if project.id %}
24 | Delete
25 | {% endif %}
26 | Return
27 | {% endblock %}
28 |
--------------------------------------------------------------------------------
/www/static/profile.js:
--------------------------------------------------------------------------------
1 | var bboxesLayer;
2 |
3 | $(function() {
4 | map = L.map('map', {minZoom: 4, maxZoom: 15, editable: true});
5 | L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
6 | attribution: '© OpenStreetMap '
7 | }).addTo(map);
8 |
9 | L.BoxControl = L.Control.extend({
10 | onAdd: function (map) {
11 | var container = L.DomUtil.create('div', 'leaflet-control leaflet-bar'),
12 | link = L.DomUtil.create('a', '', container);
13 |
14 | link.href = '#';
15 | link.title = 'Create a new box';
16 | link.innerHTML = '⬛';
17 | L.DomEvent.on(link, 'click', L.DomEvent.stop)
18 | .on(link, 'click', function () {
19 | map.editTools.startRectangle();
20 | }, this);
21 |
22 | return container;
23 | }
24 | });
25 | map.addControl(new L.BoxControl({ position: 'topleft' }));
26 |
27 | function addDeletePopup(layer) {
28 | var btn = $('Delete ');
29 | btn.click(function() {
30 | bboxesLayer.removeLayer(layer);
31 | });
32 | layer.bindPopup(btn[0]);
33 | }
34 |
35 | bboxesLayer = L.featureGroup().addTo(map);
36 | map.setView([50, 10], 4);
37 | map.editTools.featuresLayer = bboxesLayer;
38 |
39 | var bboxes_str = $('#bboxes').val(),
40 | bboxes = bboxes_str.split(';');
41 | for (var i = 0; i < bboxes.length; i++) {
42 | var c = bboxes[i].split(',');
43 | if (c.length == 4) {
44 | var rect = L.rectangle([[+c[0], +c[1]], [+c[2], +c[3]]]).addTo(bboxesLayer);
45 | addDeletePopup(rect);
46 | rect.enableEdit();
47 | }
48 | }
49 | if (bboxesLayer.getLayers().length > 0)
50 | map.fitBounds(bboxesLayer.getBounds());
51 |
52 | map.on('editable:drawing:end', function(e) {
53 | addDeletePopup(e.layer);
54 | updateBBoxes();
55 | });
56 | map.on('editable:dragend', updateBBoxes);
57 | bboxesLayer.on('layerremove', updateBBoxes);
58 | });
59 |
60 | function updateBBoxes() {
61 | var boxes = [];
62 | bboxesLayer.eachLayer(function(box) {
63 | var b = box.getBounds(),
64 | c1 = b.getSouthWest(),
65 | c2 = b.getNorthEast();
66 | boxes.push([c1.lat, c1.lng, c2.lat, c2.lng].join(','));
67 | });
68 | $('#bboxes').val(boxes.join(';'));
69 | console.log($('#bboxes').val());
70 | }
71 |
--------------------------------------------------------------------------------
/www/static/PermalinkAttribution.js:
--------------------------------------------------------------------------------
1 | /*
2 | * L.Control.Attribution that replaces OpenStreetMap links with permalinks.
3 | * Also can edd an edit link.
4 | * Replaces standard attribution control, because of https://github.com/Leaflet/Leaflet/issues/2177
5 | */
6 | L.Control.StandardAttribution = L.Control.Attribution;
7 | L.Control.PermalinkAttribution = L.Control.Attribution.extend({
8 | onAdd: function( map ) {
9 | var container = L.Control.StandardAttribution.prototype.onAdd.call(this, map);
10 | map.on('moveend', this._update, this);
11 | return container;
12 | },
13 |
14 | onRemove: function( map ) {
15 | map.off('moveend', this._update);
16 | L.Control.StandardAttribution.prototype.onRemove.call(this, map);
17 | },
18 |
19 | // copied from original class and slightly modified
20 | _update: function () {
21 | if (!this._map) { return; }
22 |
23 | var attribs = [];
24 |
25 | for (var i in this._attributions) {
26 | if (this._attributions[i]) {
27 | // make permalink for openstreetmap
28 | if( i.indexOf('/openstreetmap.org') > 0 || i.indexOf('/www.openstreetmap.org') > 0 ) {
29 | var permalink = 'http://www.openstreetmap.org/#map={zoom}/{lat}/{lon}';
30 | i = i.replace(/(['"])http[^'"]+openstreetmap.org[^'"]*(['"])/, '$1' + permalink + '$2');
31 | if( this._map.options.attributionEditLink ) {
32 | var editlink = permalink.replace('#', 'edit#');
33 | i = i.replace(/(openstreetmap.org[^'"]*(['"])[^>]*>[^<]+<\/a>)/, '$1 (Edit )');
34 | }
35 | }
36 | var latlng = this._map.getCenter();
37 | i = i.replace(/\{zoom\}/g, this._map.getZoom()).replace(/\{lat\}/g, L.Util.formatNum(latlng.lat, 4)).replace(/\{lon\}/g, L.Util.formatNum(latlng.lng, 4));
38 | attribs.push(i);
39 | }
40 | }
41 |
42 | var prefixAndAttribs = [];
43 |
44 | if (this.options.prefix) {
45 | prefixAndAttribs.push(this.options.prefix);
46 | }
47 | if (attribs.length) {
48 | prefixAndAttribs.push(attribs.join(', '));
49 | }
50 |
51 | this._container.innerHTML = prefixAndAttribs.join(' | ');
52 | }
53 | });
54 |
55 | L.control.permalinkAttribution = function( options ) {
56 | return new L.Control.PermalinkAttribution(options);
57 | };
58 |
59 | L.Map.mergeOptions({
60 | attributionEditLink: false
61 | });
62 |
63 | L.Control.Attribution = L.Control.PermalinkAttribution;
64 | L.control.standardAttribution = L.control.attribution;
65 | L.control.attribution = L.control.permalinkAttribution;
66 |
--------------------------------------------------------------------------------
/www/templates/map.html:
--------------------------------------------------------------------------------
1 | {% extends "layout.html" %}
2 | {% block title %}{{ project.title }} — {% endblock %}
3 | {% block header %}
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
27 |
28 |
29 | {% endblock %}
30 | {% block content %}
31 |
32 |
46 | {% endblock %}
47 |
--------------------------------------------------------------------------------
/www/templates/project.html:
--------------------------------------------------------------------------------
1 | {% extends "layout.html" %}
2 | {% block title %}{{ project.title }} — {% endblock %}
3 | {% block header %}
4 |
26 | {% endblock %}
27 | {% block content %}
28 | {{ project.title }}
29 | {{ desc | safe }}
30 | {% if project.url %}Additional info →
{% endif %}
31 | Browse points
32 | (on a map ,
33 | as a table )
34 | {% if project.can_validate %}
35 | Validate the import
36 | {% endif %}
37 | {% if admin %}
38 | Edit ,
39 | Download Audit ,
40 | Audit for Source
41 | {% endif %}
42 |
43 |
44 | Total features: {{ project.feature_count }}
45 | Features to validate: {{ count }}
46 | Features looked at: {{ val1 }}
47 | Validated twice: {{ val2 }}
48 | Have corrections: {{ corrected }}
49 | To be ignored: {{ skipped }}
50 |
51 | {% if has_skipped %}
52 | Put skipped items back on your review list
53 | {% endif %}
54 | {% if regions %}
55 | Filter by region:
56 |
57 | {% for r in regions %}
58 | {{ r[0] or 'All' }} ({{ r[2] }} / {{ r[1] }})
59 | {% endfor %}
60 |
61 | {% endif %}
62 | Return
63 |
69 | {% endblock %}
70 |
--------------------------------------------------------------------------------
/www/templates/browse.html:
--------------------------------------------------------------------------------
1 | {% extends "layout.html" %}
2 | {% block title %}{{ project.title }} — {% endblock %}
3 | {% block header %}
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
30 |
31 |
32 | {% endblock %}
33 | {% block content %}
34 |
35 |
← to the project {% if project.can_validate %}, edit this {% endif %}
36 |
{{ project.title }}
37 |
38 | Transparent marker is the point location.
39 |
40 |
44 |
47 |
The last reviewer rejected this change with the verdict " ".
48 |
49 | Zoom to dataset
50 | Zoom back
51 | Random feature
52 |
53 |
54 |
55 |
56 | {% endblock %}
57 |
--------------------------------------------------------------------------------
/www/static/style.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | height: 100%;
3 | margin: 0;
4 | padding: 0;
5 | }
6 |
7 | #map1, #map2 {
8 | position: fixed;
9 | right: 0;
10 | left: 35%;
11 | height: 50%;
12 | }
13 | #map1 {
14 | top: 0;
15 | }
16 | #map2 {
17 | bottom: 0;
18 | }
19 |
20 | @media screen and (max-width: 600px) {
21 | .leaflet-control-minimap { display: none !important; }
22 | }
23 |
24 | #map1.bigmap {
25 | height: 100%;
26 | position: inherit;
27 | left: 0;
28 | }
29 |
30 | #popup {
31 | display: none;
32 | }
33 |
34 | #left {
35 | width: 35%;
36 | padding: 0.5em;
37 | font-size: 14px;
38 | font-family: sans-serif;
39 | margin-bottom: 250px;
40 | line-height: 1;
41 | }
42 |
43 | #buttons {
44 | position: fixed;
45 | background: white;
46 | bottom: 0;
47 | width: 35%;
48 | }
49 |
50 | p.toproject {
51 | font-size: 12px;
52 | margin: 0;
53 | }
54 |
55 | h1 {
56 | font-size: 26px;
57 | }
58 |
59 | .hint {
60 | margin-top: 0.5em;
61 | font-style: italic;
62 | color: green;
63 | }
64 |
65 | .last_action {
66 | font-style: italic;
67 | color: darkred;
68 | }
69 |
70 | .tags_wrapper {
71 | margin: 1em 0;
72 | overflow-x: auto;
73 | width: 95%;
74 | }
75 |
76 | .tags {
77 | border-collapse: collapse;
78 | }
79 |
80 | .tags tr {
81 | border: 1px solid lightgrey;
82 | border-bottom: none;
83 | }
84 |
85 | .tags tr.lower {
86 | border-top: none;
87 | }
88 |
89 | .tags th, tr.lower, tr.notagedit {
90 | border-bottom: 1px solid lightgrey;
91 | }
92 |
93 | .tags th {
94 | text-align: left;
95 | padding-right: 1em;
96 | }
97 |
98 | .tags .tagedit td {
99 | cursor: pointer;
100 | }
101 |
102 | .tags td {
103 | padding: 2px 0;
104 | }
105 |
106 | .tags td a {
107 | color: inherit;
108 | }
109 |
110 | .tags td.red { background-color: pink; }
111 | .tags td.green { background-color: lightgreen; }
112 | .tags td.yellow { background-color: yellow; }
113 |
114 | .tags .notset {
115 | color: lightgrey;
116 | font-style: italic;
117 | }
118 |
119 | .tags td.red .notset { color: grey; }
120 |
121 | td input {
122 | float: right;
123 | height: 1em;
124 | }
125 |
126 | #fixme {
127 | width: 90%;
128 | }
129 |
130 | #buttons button {
131 | display: inline-block;
132 | width: 95%;
133 | margin-bottom: 0.5em;
134 | border: solid 1px lightgrey;
135 | font-size: 26px;
136 | }
137 |
138 | #buttons #submit_reason {
139 | width: inherit;
140 | margin: 0;
141 | font-size: 16px;
142 | vertical-align: bottom;
143 | }
144 |
145 | #reason_box p {
146 | margin: 0;
147 | }
148 |
149 | #reason_box input {
150 | width: 75%;
151 | }
152 |
153 | #reason_box #create {
154 | background-color: lightgreen;
155 | margin-top: 0.5em;
156 | }
157 |
158 | #buttons .b_good {
159 | background-color: lightgreen;
160 | height: 4em;
161 | }
162 |
163 | .b_good:hover, .b_next:hover, .b_readonly:hover {
164 | font-weight: bold;
165 | }
166 |
167 | #buttons .b_bad {
168 | background-color: white;
169 | color: pink;
170 | }
171 |
172 | #buttons #bad_dup, #buttons #bad_nosuch {
173 | width: 47%;
174 | vertical-align: bottom;
175 | }
176 |
177 | #buttons .b_bad:hover {
178 | color: red;
179 | }
180 |
181 | #buttons .b_zoom {
182 | width: 47%;
183 | height: 3em;
184 | vertical-align: bottom;
185 | }
186 |
--------------------------------------------------------------------------------
/www/templates/table.html:
--------------------------------------------------------------------------------
1 | {% extends "layout.html" %}
2 | {% block title %}{{ project.title }} — {% endblock %}
3 | {% block header %}
4 |
19 | {% endblock %}
20 | {% block content %}
21 | {{ project.title }}
22 | This table shows new and modified tags on each object. Hover over a cell to see the original value.
23 | Click on an "Edit" link to validate the feature.
24 |
25 |
26 |
27 |
28 |
29 | {% for col in columns %}
30 | {{ col }}
31 | {% endfor %}
32 |
33 | {% for row in rows %}
34 |
35 | ●
36 | {% if project.can_validate %}
37 | Edit
38 | {% else %}
39 | View
40 | {% endif %}
41 |
42 | {% for col in columns %}
43 | {% if col in row.tags %}
44 | {# TODO: v.before on hover, cross v.after if not chosen #}
45 | {% if not row.tags[col].accepted %}{% endif %}● {{ row.tags[col].after }}{% if not row.tags[col].accepted %} {% endif %}
47 | {% else %}
48 |
49 | {% endif %}
50 | {% endfor %}
51 |
52 | {% endfor %}
53 |
54 |
55 |
56 | {% if pagination.pages > 1 %}
57 |
74 | {% endif %}
75 |
76 | Legend:
77 |
78 | ● Added
79 | ● Changed
80 | ● Deleted
81 |
82 | {% if show_validated %}
83 | Hide validated objects
84 | {% else %}
85 | Include validated objects in the list
86 | {% endif %}
87 | Return
88 | {% endblock %}
89 |
--------------------------------------------------------------------------------
/www/templates/task.html:
--------------------------------------------------------------------------------
1 | {% extends "layout.html" %}
2 | {% block title %}{{ project.title }} — {% endblock %}
3 | {% block header %}
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
29 |
30 | {% endblock %}
31 | {% block content %}
32 |
33 |
← to the project , browse around
34 |
{{ project.title }}
35 |
The last reviewer rejected this change with the verdict " ".
36 |
37 | You can move the marker to a better location. Imagery may be misaligned.
38 | Semi-transparent marker is the location from .
39 |
40 |
44 |
45 |
Value for the fixme tag if needed:
46 |
47 |
48 |
51 |
64 |
65 |
66 |
67 |