Compare commits
20 Commits
0.1
...
9af55d3f24
Author | SHA1 | Date | |
---|---|---|---|
9af55d3f24 | |||
719e75a449 | |||
6688536a78 | |||
7835ddcf16 | |||
3caada8f08 | |||
4113811c5f | |||
a660763ea4 | |||
55fe79b82c | |||
b34e88a3ad | |||
f52dd733d8 | |||
ff428beaef | |||
e8c4faff8e | |||
0b0a401486 | |||
2b56eed759 | |||
e387091da6 | |||
5c5ad9502c | |||
baf3f54e1a | |||
8cb7e61274 | |||
fa1274c595 | |||
a5839c2c36 |
66
Readme.md
66
Readme.md
@@ -9,7 +9,11 @@ Right now it depends on the following non-core Python 3 libraries. These can be
|
||||
|
||||
* [django](https://www.djangoproject.com/) django is a high-level Python Web framework that encourages rapid development and clean, pragmatic design.
|
||||
* [easy-thumbnails](https://github.com/SmileyChris/easy-thumbnails) A powerful, yet easy to implement thumbnailing application for Django 1.11+
|
||||
* [image](https://github.com/francescortiz/image) Image cropping for django
|
||||
* [django-widget-tweaks](https://github.com/jazzband/django-widget-tweaks) Tweak the form field rendering in templates, not in python-level form definitions.
|
||||
|
||||
|
||||
## Development
|
||||
### Setting up a (pipenv) virtual environment for development
|
||||
|
||||
After having obtained the repository contents (either via .zip download or git clone), you can easily setup a pipenv virtual environment. The repo provides a Pipfile for easy dependency management that does not mess with your system.
|
||||
@@ -18,6 +22,7 @@ After having obtained the repository contents (either via .zip download or git c
|
||||
$ cd lostplaces-backend
|
||||
$ pipenv install
|
||||
$ pipenv shell
|
||||
(lostplaces-backend) $ lostplaces/manage.py makemigrations
|
||||
(lostplaces-backend) $ lostplaces/manage.py migrate
|
||||
(lostplaces-backend) $ lostplaces/manage.py createsuperuser
|
||||
(lostplaces-backend) $ lostplaces/manage.py runserver
|
||||
@@ -33,4 +38,65 @@ $ pipenv shell
|
||||
Visit: [admin](http://localhost:8000/admin) for administrative backend or
|
||||
[frontend](http://localhost:8000/)
|
||||
|
||||
## Installing lostplaces
|
||||
|
||||
### Install dependencies
|
||||
Python3, Django3, easy-thumbnails, image, django-widget-tweaks
|
||||
```
|
||||
pip install --user django easy-thumbnails image django-widget-tweaks
|
||||
```
|
||||
Or, if you use pipenv
|
||||
```
|
||||
pipenv install
|
||||
```
|
||||
|
||||
|
||||
### Add 'lostplaces_app' to your INSTALLED_APPS setting like this
|
||||
|
||||
```
|
||||
INSTALLED_APPS = [
|
||||
...
|
||||
'lostplaces_app',
|
||||
'easy_thumbnails',
|
||||
'widget_tweaks',
|
||||
]
|
||||
```
|
||||
|
||||
### Add this configuration to your settings.py
|
||||
|
||||
```
|
||||
from django.urls import reverse_lazy
|
||||
...
|
||||
AUTH_USER_MODEL = 'lostplaces_app.Explorer'
|
||||
|
||||
LOGIN_URL = reverse_lazy('login')
|
||||
|
||||
THUMBNAIL_ALIASES = {
|
||||
'': {
|
||||
'thumbnail': {'size': (300, 300), 'crop': False},
|
||||
'hero': {'size': (700, 700), 'crop': False},
|
||||
'large': {'size': (1920, 1920), 'crop': False},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Include the lostplaces URLconf in your project urls.py like this
|
||||
|
||||
```
|
||||
from django.urls import path, include
|
||||
...
|
||||
urlpatterns = [
|
||||
...
|
||||
path('lostplaces/', include('lostplaces_app.urls')),
|
||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
```
|
||||
|
||||
Run ``python manage.py migrate`` to create the lost places models.
|
||||
|
||||
Start the development server and visit http://127.0.0.1:8000/admin/
|
||||
|
||||
Visit http://127.0.0.1:8000/lostplaces/ to CRUD lost places.
|
||||
|
||||
|
||||
Happy developing ;-)
|
||||
|
@@ -55,6 +55,19 @@ class Place (models.Model):
|
||||
longitude = models.FloatField()
|
||||
description = models.TextField()
|
||||
|
||||
# Get center position of all LP-geocoordinates.
|
||||
|
||||
def average_latlon():
|
||||
place_list = Place.objects.all()
|
||||
amount = len(place_list)
|
||||
longitude = 0
|
||||
latitude = 0
|
||||
|
||||
for place in place_list:
|
||||
longitude += place.longitude
|
||||
latitude += place.latitude
|
||||
return (latitude / amount, longitude / amount)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
2
lostplaces/lostplaces_app/static/ol.css
Normal file
2
lostplaces/lostplaces_app/static/ol.css
Normal file
@@ -0,0 +1,2 @@
|
||||
.ol-box{box-sizing:border-box;border-radius:2px;border:2px solid #00f}.ol-mouse-position{top:8px;right:8px;position:absolute}.ol-scale-line{background:rgba(0,60,136,.3);border-radius:4px;bottom:8px;left:8px;padding:2px;position:absolute}.ol-scale-line-inner{border:1px solid #eee;border-top:none;color:#eee;font-size:10px;text-align:center;margin:1px;will-change:contents,width;transition:all .25s}.ol-scale-bar{position:absolute;bottom:8px;left:8px}.ol-scale-step-marker{width:1px;height:15px;background-color:#000;float:right;z-Index:10}.ol-scale-step-text{position:absolute;bottom:-5px;font-size:12px;z-Index:11;color:#000;text-shadow:-2px 0 #fff,0 2px #fff,2px 0 #fff,0 -2px #fff}.ol-scale-text{position:absolute;font-size:14px;text-align:center;bottom:25px;color:#000;text-shadow:-2px 0 #fff,0 2px #fff,2px 0 #fff,0 -2px #fff}.ol-scale-singlebar{position:relative;height:10px;z-Index:9;border:1px solid #000}.ol-unsupported{display:none}.ol-unselectable,.ol-viewport{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent}.ol-overlaycontainer,.ol-overlaycontainer-stopevent{pointer-events:none}.ol-overlaycontainer-stopevent>*,.ol-overlaycontainer>*{pointer-events:auto}.ol-selectable{-webkit-touch-callout:default;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}.ol-grabbing{cursor:-webkit-grabbing;cursor:-moz-grabbing;cursor:grabbing}.ol-grab{cursor:move;cursor:-webkit-grab;cursor:-moz-grab;cursor:grab}.ol-control{position:absolute;background-color:rgba(255,255,255,.4);border-radius:4px;padding:2px}.ol-control:hover{background-color:rgba(255,255,255,.6)}.ol-zoom{top:.5em;left:.5em}.ol-rotate{top:.5em;right:.5em;transition:opacity .25s linear,visibility 0s linear}.ol-rotate.ol-hidden{opacity:0;visibility:hidden;transition:opacity .25s linear,visibility 0s linear .25s}.ol-zoom-extent{top:4.643em;left:.5em}.ol-full-screen{right:.5em;top:.5em}.ol-control button{display:block;margin:1px;padding:0;color:#fff;font-size:1.14em;font-weight:700;text-decoration:none;text-align:center;height:1.375em;width:1.375em;line-height:.4em;background-color:rgba(0,60,136,.5);border:none;border-radius:2px}.ol-control button::-moz-focus-inner{border:none;padding:0}.ol-control button span{pointer-events:none}.ol-zoom-extent button{line-height:1.4em}.ol-compass{display:block;font-weight:400;font-size:1.2em;will-change:transform}.ol-touch .ol-control button{font-size:1.5em}.ol-touch .ol-zoom-extent{top:5.5em}.ol-control button:focus,.ol-control button:hover{text-decoration:none;background-color:rgba(0,60,136,.7)}.ol-zoom .ol-zoom-in{border-radius:2px 2px 0 0}.ol-zoom .ol-zoom-out{border-radius:0 0 2px 2px}.ol-attribution{text-align:right;bottom:.5em;right:.5em;max-width:calc(100% - 1.3em)}.ol-attribution ul{margin:0;padding:0 .5em;color:#000;text-shadow:0 0 2px #fff}.ol-attribution li{display:inline;list-style:none}.ol-attribution li:not(:last-child):after{content:" "}.ol-attribution img{max-height:2em;max-width:inherit;vertical-align:middle}.ol-attribution button,.ol-attribution ul{display:inline-block}.ol-attribution.ol-collapsed ul{display:none}.ol-attribution:not(.ol-collapsed){background:rgba(255,255,255,.8)}.ol-attribution.ol-uncollapsible{bottom:0;right:0;border-radius:4px 0 0}.ol-attribution.ol-uncollapsible img{margin-top:-.2em;max-height:1.6em}.ol-attribution.ol-uncollapsible button{display:none}.ol-zoomslider{top:4.5em;left:.5em;height:200px}.ol-zoomslider button{position:relative;height:10px}.ol-touch .ol-zoomslider{top:5.5em}.ol-overviewmap{left:.5em;bottom:.5em}.ol-overviewmap.ol-uncollapsible{bottom:0;left:0;border-radius:0 4px 0 0}.ol-overviewmap .ol-overviewmap-map,.ol-overviewmap button{display:inline-block}.ol-overviewmap .ol-overviewmap-map{border:1px solid #7b98bc;height:150px;margin:2px;width:150px}.ol-overviewmap:not(.ol-collapsed) button{bottom:1px;left:2px;position:absolute}.ol-overviewmap.ol-collapsed .ol-overviewmap-map,.ol-overviewmap.ol-uncollapsible button{display:none}.ol-overviewmap:not(.ol-collapsed){background:rgba(255,255,255,.8)}.ol-overviewmap-box{border:2px dotted rgba(0,60,136,.7)}.ol-overviewmap .ol-overviewmap-box:hover{cursor:move}
|
||||
/*# sourceMappingURL=ol.css.map */
|
2
lostplaces/lostplaces_app/static/ol.js
Normal file
2
lostplaces/lostplaces_app/static/ol.js
Normal file
File diff suppressed because one or more lines are too long
1
lostplaces/lostplaces_app/static/ol.js.map
Normal file
1
lostplaces/lostplaces_app/static/ol.js.map
Normal file
File diff suppressed because one or more lines are too long
@@ -12,19 +12,6 @@
|
||||
|
||||
{% block additional_head %}
|
||||
{% endblock additional_head %}
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function(){
|
||||
Array.from(document.getElementsByClassName('LP-Main__Sidebar')).forEach(function(element){
|
||||
element.classList.add('LP-Main__Sidebar--hidden')
|
||||
})
|
||||
setTimeout(function(){
|
||||
Array.from(document.getElementsByClassName('LP-Main__Sidebar')).forEach(function(element){
|
||||
element.classList.remove('LP-Main__Sidebar--hidden')
|
||||
})
|
||||
}, 500)
|
||||
})
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
|
@@ -1,10 +1,15 @@
|
||||
{% extends 'global.html'%}
|
||||
{% load static %}
|
||||
{% block additional_head %}
|
||||
<link rel="stylesheet" href="{% static 'ol.css' %}" type="text/css">
|
||||
<script src="{% static 'ol.js' %}"></script>
|
||||
{% endblock additional_head %}
|
||||
|
||||
# {% block title %}Start{% endblock %}
|
||||
|
||||
{% block maincontent %}
|
||||
|
||||
{% include 'partials/osm_map.html' %}
|
||||
<div class="LP-PlaceGrid">
|
||||
<h1 class="LP-Headline LP-Headline">Explore the latest locations</h1>
|
||||
<ul class="LP-PlaceGrid__Grid">
|
||||
|
70
lostplaces/lostplaces_app/templates/partials/osm_map.html
Normal file
70
lostplaces/lostplaces_app/templates/partials/osm_map.html
Normal file
@@ -0,0 +1,70 @@
|
||||
{% load static %}
|
||||
|
||||
<div id="map" class="map"></div>
|
||||
<div id="info" class="map-popup"></div>
|
||||
|
||||
<script type="text/javascript">
|
||||
var map = new ol.Map({
|
||||
target: 'map',
|
||||
layers: [
|
||||
new ol.layer.Tile({
|
||||
source: new ol.source.OSM()
|
||||
}),
|
||||
],
|
||||
view: new ol.View({
|
||||
center: ol.proj.fromLonLat([{{place_map_center|last}}, {{place_map_center|first}}]),
|
||||
zoom: 9
|
||||
})
|
||||
});
|
||||
|
||||
var vectorSource = new ol.source.Vector({
|
||||
features: [
|
||||
{% for place in place_list %}
|
||||
new ol.Feature({
|
||||
geometry: new ol.geom.Point(
|
||||
ol.proj.fromLonLat([{{place.longitude}},{{place.latitude}}])
|
||||
),
|
||||
url: '{% url 'place_detail' pk=place.pk %}',
|
||||
name: '{{place.name}}'
|
||||
}),
|
||||
{% endfor %}
|
||||
]
|
||||
});
|
||||
|
||||
var markerVectorLayer = new ol.layer.Vector({
|
||||
source: vectorSource,
|
||||
style: new ol.style.Style({
|
||||
image: new ol.style.Icon({
|
||||
anchor: [0.5, 46],
|
||||
anchorXUnits: 'fraction',
|
||||
anchorYUnits: 'pixels',
|
||||
scale: 0.3,
|
||||
src: 'http://icons.iconarchive.com/icons/paomedia/small-n-flat/128/map-marker-icon.png'
|
||||
})
|
||||
})
|
||||
});
|
||||
map.addLayer(markerVectorLayer);
|
||||
|
||||
var overlay = new ol.Overlay({
|
||||
element: document.getElementById('info'),
|
||||
positioning: 'bottom-left'
|
||||
});
|
||||
overlay.setMap(map);
|
||||
|
||||
map.on(['singleclick'], function(evt) {
|
||||
var feature = map.forEachFeatureAtPixel(evt.pixel, function(feature) {
|
||||
window.open(feature.get('url'), '_blank');
|
||||
});
|
||||
});
|
||||
|
||||
map.on(['pointermove'], function(evt) {
|
||||
var feature = map.forEachFeatureAtPixel(evt.pixel, function(feature) {
|
||||
overlay.setPosition(evt.coordinate.map(element => element + 1));
|
||||
overlay.getElement().innerHTML = feature.get('name');
|
||||
return feature;
|
||||
});
|
||||
overlay.getElement().style.display = feature ? '' : 'none';
|
||||
document.body.style.cursor = feature ? 'pointer' : '';
|
||||
});
|
||||
|
||||
</script>
|
@@ -50,7 +50,7 @@ class SignUpView(SuccessMessageMixin, CreateView):
|
||||
success_message = 'User created.'
|
||||
|
||||
class PlaceListView(IsAuthenticated, ListView):
|
||||
paginate_by = 2
|
||||
paginate_by = 5
|
||||
model = Place
|
||||
template_name = 'place/place_list.html'
|
||||
|
||||
@@ -64,8 +64,10 @@ class PlaceDetailView(IsAuthenticated, View):
|
||||
class HomeView(View):
|
||||
def get(self, request, *args, **kwargs):
|
||||
place_list = Place.objects.all().order_by('-submitted_when')[:10]
|
||||
place_map_center = Place.average_latlon()
|
||||
context = {
|
||||
'place_list': place_list
|
||||
'place_list': place_list,
|
||||
'place_map_center': place_map_center
|
||||
}
|
||||
return render(request, 'home.html', context)
|
||||
|
||||
|
Reference in New Issue
Block a user