diff options
-rw-r--r-- | app/builder/base.py | 6 | ||||
-rw-r--r-- | app/builder/views.py | 6 | ||||
-rw-r--r-- | app/jrnl/build.py | 15 | ||||
-rw-r--r-- | app/locations/urls.py | 9 | ||||
-rw-r--r-- | app/locations/views.py | 37 | ||||
-rw-r--r-- | config/base_urls.py | 12 | ||||
-rw-r--r-- | design/templates/archives/map.html | 11 | ||||
-rw-r--r-- | design/templates/archives/map_data.html | 475 |
8 files changed, 557 insertions, 14 deletions
diff --git a/app/builder/base.py b/app/builder/base.py index 67a259e..d9c2288 100644 --- a/app/builder/base.py +++ b/app/builder/base.py @@ -20,8 +20,6 @@ class _FileWriter(object): os.makedirs(self.path) fpath = '%s%s.%s' % (self.path, filename, ext) self.write(fpath, text_object) - if ext == 'js': - self.compress_js(filename, text_object) def write(self, fpath, text_object): f = open(fpath, 'wb') @@ -30,7 +28,7 @@ class _FileWriter(object): def compress_js(self, filename, text_object): path = '%s%s.min.js' % (self.path, filename) - compressed = jsmin(text_object.decode(encoding='UTF-8')) + compressed = jsmin(text_object.decode('utf-8')).encode('utf-8') self.write(path, compressed) @@ -49,7 +47,7 @@ class BuildNew(): def write_file(self, path, text_object, ext='html', filename='index'): self.writer = _FileWriter(path, text_object, ext=ext, filename=filename) - + def get_pages(self, qs, paginate_by): return int(ceil(Decimal(qs.count()) / Decimal(paginate_by))) diff --git a/app/builder/views.py b/app/builder/views.py index 1875a91..55a037c 100644 --- a/app/builder/views.py +++ b/app/builder/views.py @@ -2,7 +2,7 @@ from django.shortcuts import render_to_response from django.template import RequestContext from builder.base import BuildWriting, BuildWritingFeed, BuildMap, BuildPhotos, BuildProjects, BuildSitemap from src.build import builder as src_builder -from jrnl.build import archive_builder, detail_builder, home_builder, rss_builder, amp_builder +from jrnl.build import archive_builder, detail_builder, home_builder, rss_builder, amp_builder, map_builder from resume.build import builder as resume_builder from books.build import builder as book_builder from birds.build import builder as bird_builder @@ -15,7 +15,6 @@ options = { 'writing': BuildWriting, 'photo_galleries': BuildPhotos, 'projects': BuildProjects, - 'map': BuildMap, 'feed': BuildWritingFeed, 'sitemap': BuildSitemap, } @@ -60,6 +59,9 @@ def do_build(request): elif section == 'pages': context = {'message': 'Writing Pages to Disk'} page_builder() + elif section == 'map': + context = {'message': 'Writing Map to Disk'} + map_builder() else: options[section]().build() context = {'message': 'Writing %s to Disk' % section} diff --git a/app/jrnl/build.py b/app/jrnl/build.py index 1bc5d3e..0354470 100644 --- a/app/jrnl/build.py +++ b/app/jrnl/build.py @@ -3,6 +3,7 @@ from django.apps import apps from builder.base import BuildNew from itertools import chain +from django.conf import settings class BuildJrnl(BuildNew): @@ -56,6 +57,16 @@ class BuildJrnl(BuildNew): response = self.client.get('/') self.write_file('', response.content) + def build_map(self): + self.build_list_view( + base_path=reverse("locations:maplist"), + paginate_by=1000000 + ) + url = "/media/js/mainmap.js" + response = self.client.get("/media/js/mainmap.js", HTTP_HOST='127.0.0.1') + self.write_file("media/js/mainmap", response.content, 'js', '') + + def archive_builder(): j = BuildJrnl("jrnl", "entry") @@ -81,3 +92,7 @@ def amp_builder(): j = BuildJrnl("jrnl", "entry") j.build_amp_view() + +def map_builder(): + j = BuildJrnl("jrnl", "entry") + j.build_map() diff --git a/app/locations/urls.py b/app/locations/urls.py index 77f9f0c..72a88de 100644 --- a/app/locations/urls.py +++ b/app/locations/urls.py @@ -2,7 +2,14 @@ from django.conf.urls import url from . import views +app_name = "locations" + urlpatterns = [ url(r'data/(?P<id>\d+)/$', views.data_json), - url(r'^$', views.map_list), + url( + r'^$', + views.MapList.as_view(), + name="maplist" + ), + #url(r'^$', views.map_list), ] diff --git a/app/locations/views.py b/app/locations/views.py index 12901eb..c463eac 100644 --- a/app/locations/views.py +++ b/app/locations/views.py @@ -4,6 +4,9 @@ from jrnl.models import Entry from locations.models import Country, Region, Route from projects.shortcuts import render_to_geojson +from django.views.generic import ListView +from django.conf import settings + def map_list(request): context = { @@ -18,6 +21,40 @@ def map_list(request): context_instance=RequestContext(request) ) +class MapList(ListView): + """ + Return list of Entries on map + """ + context_object_name = 'object_list' + queryset = Entry.objects.filter(status__exact=1) + template_name = 'archives/map.html' + + def get_context_data(self, **kwargs): + # Call the base implementation first to get a context + context = super(MapList, self).get_context_data(**kwargs) + context['country_list'] = Country.objects.filter(visited=True).exclude(name='default'), + context['route_list'] = Route.objects.all(), + context['region_list'] = Region.objects.all() + context['IMAGES_URL'] = settings.IMAGES_URL + return context + + +class MapDataList(ListView): + """ + Build data file for Entries on map + """ + context_object_name = 'object_list' + queryset = Entry.objects.filter(status__exact=1) + template_name = 'archives/map_data.html' + + def get_context_data(self, **kwargs): + # Call the base implementation first to get a context + context = super(MapDataList, self).get_context_data(**kwargs) + context['country_list'] = Country.objects.filter(visited=True).exclude(name='default'), + context['route_list'] = Route.objects.all(), + context['region_list'] = Region.objects.all() + context['IMAGES_URL'] = settings.IMAGES_URL + return context def map_data(request): context = { diff --git a/config/base_urls.py b/config/base_urls.py index 0f63a75..aa97cbd 100644 --- a/config/base_urls.py +++ b/config/base_urls.py @@ -3,6 +3,7 @@ from django.contrib import admin from django.views import static from django.conf import settings from django.contrib.sitemaps.views import sitemap +from django.views.generic import TemplateView from pages.views import page from pages.views import PageDetailView @@ -18,6 +19,8 @@ from syndication.views import FacebookFeedView import builder.views import utils.views +from locations.views import MapDataList + admin.autodiscover() sitemaps = { @@ -30,7 +33,14 @@ sitemaps = { urlpatterns = [ url(r'^projects/data/natparks/(?P<path>.*)$', static.serve, {'document_root': settings.PROJ_ROOT + 'site/projects/data/natparks/'}), - url(r'^media/js/mainmap.js$', map_data), + url( + r'^media/js/mainmap.js$', + MapDataList.as_view(), + name="mapdatalist" + ), + url (r'media/js/leaflet-providers.js', + TemplateView.as_view(template_name='js/leaflet-providers.js') + ), url(r'^media/admin/(?P<path>.*)$', static.serve, {'document_root': settings.MEDIA_ROOT + '/admin'}), url(r'^media/(?P<path>.*)$', static.serve, {'document_root': settings.MEDIA_ROOT}), ] diff --git a/design/templates/archives/map.html b/design/templates/archives/map.html index a40e977..8195e6d 100644 --- a/design/templates/archives/map.html +++ b/design/templates/archives/map.html @@ -29,9 +29,9 @@ Google Maps code <div class="map-legend"> <h4>Trips</h4> <ul> - {% for route in route_list %} + {% for r in route_list %}{%for route in r %} <li><a onclick="showRoute('{{route.template_var_name}}', {{route.zoom}}, '{{route.geometry.centroid.y}}','{{route.geometry.centroid.x}}');" href="#" title="show {{route.name}} on map">{{route.name}}</a></li> - {% endfor %} + {% endfor %}{% endfor %} </ul> <h4>Regions</h4> <ul> @@ -42,15 +42,16 @@ Google Maps code <h4>Countries</h4> <ul> <li><a onclick="focusCountry(19.311143,2.460938,2);" href="#" title="view all countries">All</a></li> - {%for country in country_list %} + {%for c in country_list %} + {% for country in c %} <li><a href="#{{country.slug}}" onclick="focusCountry({{country.lat}}, {{country.lon}}, {{country.zoom_level}});" title="See all writing from {{country.name|title}}">{{country.name}}</a></li> {% endfor %} + {% endfor %} </ul> </div> - </section> {% endblock %} {% block js %} <!--<script type="text/javascript" src="/media/js/mainmap.js"></script>--> -<script type="text/javascript" src="/media/js/mainmap.min.js"></script> +<script type="text/javascript" src="/media/js/mainmap.js"></script> {% endblock%} diff --git a/design/templates/archives/map_data.html b/design/templates/archives/map_data.html index 120704c..8e9c6d0 100644 --- a/design/templates/archives/map_data.html +++ b/design/templates/archives/map_data.html @@ -1,5 +1,476 @@ {% load typogrify_tags %} {% load truncateletters %} +(function () { + 'use strict'; + + L.TileLayer.Provider = L.TileLayer.extend({ + initialize: function (arg, options) { + var providers = L.TileLayer.Provider.providers; + + var parts = arg.split('.'); + + var providerName = parts[0]; + var variantName = parts[1]; + + if (!providers[providerName]) { + throw 'No such provider (' + providerName + ')'; + } + + var provider = { + url: providers[providerName].url, + options: providers[providerName].options + }; + + // overwrite values in provider from variant. + if (variantName && 'variants' in providers[providerName]) { + if (!(variantName in providers[providerName].variants)) { + throw 'No such variant of ' + providerName + ' (' + variantName + ')'; + } + var variant = providers[providerName].variants[variantName]; + var variantOptions; + if (typeof variant === 'string') { + variantOptions = { + variant: variant + }; + } else { + variantOptions = variant.options; + } + provider = { + url: variant.url || provider.url, + options: L.Util.extend({}, provider.options, variantOptions) + }; + } else if (typeof provider.url === 'function') { + provider.url = provider.url(parts.splice(1, parts.length - 1).join('.')); + } + + // replace attribution placeholders with their values from toplevel provider attribution, + // recursively + var attributionReplacer = function (attr) { + if (attr.indexOf('{attribution.') === -1) { + return attr; + } + return attr.replace(/\{attribution.(\w*)\}/, + function (match, attributionName) { + return attributionReplacer(providers[attributionName].options.attribution); + } + ); + }; + provider.options.attribution = attributionReplacer(provider.options.attribution); + + // Compute final options combining provider options with any user overrides + var layerOpts = L.Util.extend({}, provider.options, options); + L.TileLayer.prototype.initialize.call(this, provider.url, layerOpts); + } + }); + + /** + * Definition of providers. + * see http://leafletjs.com/reference.html#tilelayer for options in the options map. + */ + + //jshint maxlen:220 + L.TileLayer.Provider.providers = { + OpenStreetMap: { + url: 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', + options: { + attribution: + '© <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, ' + + '<a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>' + }, + variants: { + Mapnik: {}, + BlackAndWhite: { + url: 'http://{s}.www.toolserver.org/tiles/bw-mapnik/{z}/{x}/{y}.png' + }, + DE: { + url: 'http://{s}.tile.openstreetmap.de/tiles/osmde/{z}/{x}/{y}.png' + }, + HOT: { + url: 'http://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png', + options: { + attribution: '{attribution.OpenStreetMap}, Tiles courtesy of <a href="http://hot.openstreetmap.org/" target="_blank">Humanitarian OpenStreetMap Team</a>' + } + } + } + }, + OpenSeaMap: { + url: 'http://tiles.openseamap.org/seamark/{z}/{x}/{y}.png', + options: { + attribution: 'Map data: © <a href="http://www.openseamap.org">OpenSeaMap</a> contributors' + } + }, + Thunderforest: { + url: 'http://{s}.tile.thunderforest.com/{variant}/{z}/{x}/{y}.png', + options: { + attribution: + '© <a href="http://www.opencyclemap.org">OpenCycleMap</a>, {attribution.OpenStreetMap}', + variant: 'cycle' + }, + variants: { + OpenCycleMap: 'cycle', + Transport: 'transport', + Landscape: 'landscape', + Outdoors: 'outdoors' + } + }, + OpenMapSurfer: { + url: 'http://openmapsurfer.uni-hd.de/tiles/{variant}/x={x}&y={y}&z={z}', + options: { + minZoom: 0, + maxZoom: 20, + variant: 'roads', + attribution: 'Imagery from <a href="http://giscience.uni-hd.de/">GIScience Research Group @ University of Heidelberg</a> — Map data {attribution.OpenStreetMap}' + }, + variants: { + Roads: 'roads', + AdminBounds: { + options: { + variant: 'adminb', + maxZoom: 19 + } + }, + Grayscale: { + options: { + variant: 'roadsg', + maxZoom: 19 + } + } + } + }, + MapQuestOpen: { + url: 'http://otile{s}.mqcdn.com/tiles/1.0.0/map/{z}/{x}/{y}.jpeg', + options: { + attribution: + 'Tiles Courtesy of <a href="http://www.mapquest.com/">MapQuest</a> — ' + + 'Map data {attribution.OpenStreetMap}', + subdomains: '1234' + }, + variants: { + OSM: {}, + Aerial: { + url: 'http://oatile{s}.mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.jpg', + options: { + attribution: + 'Tiles Courtesy of <a href="http://www.mapquest.com/">MapQuest</a> — ' + + 'Portions Courtesy NASA/JPL-Caltech and U.S. Depart. of Agriculture, Farm Service Agency' + } + } + } + }, + MapBox: { + url: function (id) { + return 'http://{s}.tiles.mapbox.com/v3/' + id + '/{z}/{x}/{y}.png'; + }, + options: { + attribution: + 'Imagery from <a href="http://mapbox.com/about/maps/">MapBox</a> — ' + + 'Map data {attribution.OpenStreetMap}', + subdomains: 'abcd' + } + }, + Stamen: { + url: 'http://{s}.tile.stamen.com/{variant}/{z}/{x}/{y}.png', + options: { + attribution: + 'Map tiles by <a href="http://stamen.com">Stamen Design</a>, ' + + '<a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a> — ' + + 'Map data {attribution.OpenStreetMap}', + subdomains: 'abcd', + minZoom: 0, + maxZoom: 20, + variant: 'toner' + }, + variants: { + Toner: 'toner', + TonerBackground: 'toner-background', + TonerHybrid: 'toner-hybrid', + TonerLines: 'toner-lines', + TonerLabels: 'toner-labels', + TonerLite: 'toner-lite', + Terrain: { + options: { + variant: 'terrain', + minZoom: 4, + maxZoom: 18 + } + }, + TerrainBackground: { + options: { + variant: 'terrain-background', + minZoom: 4, + maxZoom: 18 + } + }, + Watercolor: { + options: { + variant: 'watercolor', + minZoom: 3, + maxZoom: 16 + } + } + } + }, + Esri: { + url: 'https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{z}/{y}/{x}', + options: { + variant: 'World_Street_Map', + attribution: 'Tiles © Esri' + }, + variants: { + WorldStreetMap: { + options: { + attribution: + '{attribution.Esri} — ' + + 'Source: Esri, DeLorme, NAVTEQ, USGS, Intermap, iPC, NRCAN, Esri Japan, METI, Esri China (Hong Kong), Esri (Thailand), TomTom, 2012' + } + }, + DeLorme: { + options: { + variant: 'Specialty/DeLorme_World_Base_Map', + minZoom: 1, + maxZoom: 11, + attribution: '{attribution.Esri} — Copyright: ©2012 DeLorme' + } + }, + WorldTopoMap: { + options: { + variant: 'World_Topo_Map', + attribution: + '{attribution.Esri} — ' + + 'Esri, DeLorme, NAVTEQ, TomTom, Intermap, iPC, USGS, FAO, NPS, NRCAN, GeoBase, Kadaster NL, Ordnance Survey, Esri Japan, METI, Esri China (Hong Kong), and the GIS User Community' + } + }, + WorldImagery: { + options: { + variant: 'World_Imagery', + attribution: + '{attribution.Esri} — ' + + 'Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community' + } + }, + WorldTerrain: { + options: { + variant: 'World_Terrain_Base', + maxZoom: 13, + attribution: + '{attribution.Esri} — ' + + 'Source: USGS, Esri, TANA, DeLorme, and NPS' + } + }, + WorldShadedRelief: { + options: { + variant: 'World_Shaded_Relief', + maxZoom: 13, + attribution: '{attribution.Esri} — Source: Esri' + } + }, + WorldPhysical: { + options: { + variant: 'World_Physical_Map', + maxZoom: 8, + attribution: '{attribution.Esri} — Source: US National Park Service' + } + }, + OceanBasemap: { + options: { + variant: 'Ocean_Basemap', + maxZoom: 13, + attribution: '{attribution.Esri} — Sources: GEBCO, NOAA, CHS, OSU, UNH, CSUMB, National Geographic, DeLorme, NAVTEQ, and Esri' + } + }, + NatGeoWorldMap: { + options: { + variant: 'NatGeo_World_Map', + maxZoom: 16, + attribution: '{attribution.Esri} — National Geographic, Esri, DeLorme, NAVTEQ, UNEP-WCMC, USGS, NASA, ESA, METI, NRCAN, GEBCO, NOAA, iPC' + } + }, + WorldGrayCanvas: { + options: { + variant: 'Canvas/World_Light_Gray_Base', + maxZoom: 16, + attribution: '{attribution.Esri} — Esri, DeLorme, NAVTEQ' + } + } + } + }, + OpenWeatherMap: { + url: 'http://{s}.tile.openweathermap.org/map/{variant}/{z}/{x}/{y}.png', + options: { + attribution: 'Map data © <a href="http://openweathermap.org">OpenWeatherMap</a>', + opacity: 0.5 + }, + variants: { + Clouds: 'clouds', + CloudsClassic: 'clouds_cls', + Precipitation: 'precipitation', + PrecipitationClassic: 'precipitation_cls', + Rain: 'rain', + RainClassic: 'rain_cls', + Pressure: 'pressure', + PressureContour: 'pressure_cntr', + Wind: 'wind', + Temperature: 'temp', + Snow: 'snow' + } + }, + HERE: { + /* + * HERE maps, formerly Nokia maps. + * These basemaps are free, but you need an API key. Please sign up at + * http://developer.here.com/getting-started + * + * Note that the base urls contain '.cit' whichs is HERE's + * 'Customer Integration Testing' environment. Please remove for production + * envirionments. + */ + url: + 'http://{s}.{base}.maps.cit.api.here.com/maptile/2.1/' + + 'maptile/{mapID}/{variant}/{z}/{x}/{y}/256/png8?' + + 'app_id={app_id}&app_code={app_code}', + options: { + attribution: + 'Map © 1987-2014 <a href="http://developer.here.com">HERE</a>', + subdomains: '1234', + mapID: 'newest', + 'app_id': '<insert your app_id here>', + 'app_code': '<insert your app_code here>', + base: 'base', + variant: 'normal.day', + minZoom: 0, + maxZoom: 20 + }, + variants: { + normalDay: 'normal.day', + normalDayCustom: 'normal.day.custom', + normalDayGrey: 'normal.day.grey', + normalDayMobile: 'normal.day.mobile', + normalDayGreyMobile: 'normal.day.grey.mobile', + normalDayTransit: 'normal.day.transit', + normalDayTransitMobile: 'normal.day.transit.mobile', + normalNight: 'normal.night', + normalNightMobile: 'normal.night.mobile', + normalNightGrey: 'normal.night.grey', + normalNightGreyMobile: 'normal.night.grey.mobile', + + carnavDayGrey: 'carnav.day.grey', + hybridDay: { + options: { + base: 'aerial', + variant: 'hybrid.day' + } + }, + hybridDayMobile: { + options: { + base: 'aerial', + variant: 'hybrid.day.mobile' + } + }, + pedestrianDay: 'pedestrian.day', + pedestrianNight: 'pedestrian.night', + satelliteDay: { + options: { + base: 'aerial', + variant: 'satellite.day' + } + }, + terrainDay: { + options: { + base: 'aerial', + variant: 'terrain.day' + } + }, + terrainDayMobile: { + options: { + base: 'aerial', + variant: 'terrain.day.mobile' + } + } + } + }, + Acetate: { + url: 'http://a{s}.acetate.geoiq.com/tiles/{variant}/{z}/{x}/{y}.png', + options: { + attribution: + '©2012 Esri & Stamen, Data from OSM and Natural Earth', + subdomains: '0123', + minZoom: 2, + maxZoom: 18, + variant: 'acetate-base' + }, + variants: { + basemap: 'acetate-base', + terrain: 'terrain', + all: 'acetate-hillshading', + foreground: 'acetate-fg', + roads: 'acetate-roads', + labels: 'acetate-labels', + hillshading: 'hillshading' + } + } + }; + + L.tileLayer.provider = function (provider, options) { + return new L.TileLayer.Provider(provider, options); + }; + + L.Control.Layers.Provided = L.Control.Layers.extend({ + initialize: function (base, overlay, options) { + var first; + + var labelFormatter = function (label) { + return label.replace(/\./g, ': ').replace(/([a-z])([A-Z])/g, '$1 $2'); + }; + + if (base.length) { + (function () { + var out = {}, + len = base.length, + i = 0; + + while (i < len) { + if (typeof base[i] === 'string') { + if (i === 0) { + first = L.tileLayer.provider(base[0]); + out[labelFormatter(base[i])] = first; + } else { + out[labelFormatter(base[i])] = L.tileLayer.provider(base[i]); + } + } + i++; + } + base = out; + }()); + this._first = first; + } + + if (overlay && overlay.length) { + (function () { + var out = {}, + len = overlay.length, + i = 0; + + while (i < len) { + if (typeof overlay[i] === 'string') { + out[labelFormatter(overlay[i])] = L.tileLayer.provider(overlay[i]); + } + i++; + } + overlay = out; + }()); + } + L.Control.Layers.prototype.initialize.call(this, base, overlay, options); + }, + onAdd: function (map) { + this._first.addTo(map); + return L.Control.Layers.prototype.onAdd.call(this, map); + } + }); + + L.control.layers.provided = function (baseLayers, overlays, options) { + return new L.Control.Layers.Provided(baseLayers, overlays, options); + }; +}()); var map = L.map('map-inner-canvas') @@ -9,13 +480,15 @@ function focusCountry(latitude, longitude, zoom) { map.setZoom(zoom); }; -{% for route in route_list %} +{% for r in route_list %} +{% for route in r %} var {{route.template_var_name}} = L.polygon([ {% for point in route.geometry.coords%} [{{point.1}}, {{point.0}}]{% if forloop.last%}{%else%},{%endif%} {% endfor %} ]); {% endfor %} + {% endfor %} function showRoute(route, zoom, latitude, longitude) { map.panTo(new L.LatLng(latitude, longitude)); |