From 01e644c2a3667a9fb5d2977a51e4730e5176b4c5 Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Mon, 1 Apr 2013 13:37:05 +0200
Subject: Add Leaflet JS library
---
chimere/static/leaflet/LICENSE | 23 ++
chimere/static/leaflet/README.md | 28 ++
chimere/static/leaflet/images/layers.png | Bin 0 -> 973 bytes
chimere/static/leaflet/images/marker-icon.png | Bin 0 -> 1747 bytes
chimere/static/leaflet/images/marker-icon@2x.png | Bin 0 -> 4033 bytes
chimere/static/leaflet/images/marker-shadow.png | Bin 0 -> 797 bytes
chimere/static/leaflet/leaflet.css | 457 +++++++++++++++++++++++
chimere/static/leaflet/leaflet.ie.css | 57 +++
chimere/static/leaflet/leaflet.js | 8 +
9 files changed, 573 insertions(+)
create mode 100644 chimere/static/leaflet/LICENSE
create mode 100644 chimere/static/leaflet/README.md
create mode 100644 chimere/static/leaflet/images/layers.png
create mode 100644 chimere/static/leaflet/images/marker-icon.png
create mode 100644 chimere/static/leaflet/images/marker-icon@2x.png
create mode 100644 chimere/static/leaflet/images/marker-shadow.png
create mode 100644 chimere/static/leaflet/leaflet.css
create mode 100644 chimere/static/leaflet/leaflet.ie.css
create mode 100644 chimere/static/leaflet/leaflet.js
diff --git a/chimere/static/leaflet/LICENSE b/chimere/static/leaflet/LICENSE
new file mode 100644
index 0000000..46cd121
--- /dev/null
+++ b/chimere/static/leaflet/LICENSE
@@ -0,0 +1,23 @@
+Copyright (c) 2010-2013, Vladimir Agafonkin
+Copyright (c) 2010-2011, CloudMade
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are
+permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this list of
+ conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ of conditions and the following disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/chimere/static/leaflet/README.md b/chimere/static/leaflet/README.md
new file mode 100644
index 0000000..e974712
--- /dev/null
+++ b/chimere/static/leaflet/README.md
@@ -0,0 +1,28 @@
+
+
+Leaflet is a modern open-source JavaScript library for **mobile-friendly interactive maps**.
+It is developed by [Vladimir Agafonkin][] with a team of dedicated [contributors][].
+Weighing just about 27 KB of gzipped JS code, it has all the [features][] most developers ever need for online maps.
+
+Leaflet is designed with *simplicity*, *performance* and *usability* in mind.
+It works efficiently across all major desktop and mobile platforms out of the box,
+taking advantage of HTML5 and CSS3 on modern browsers while being accessible on older ones too.
+It can also be extended with many [plugins][],
+has a beautiful, easy to use and [well-documented][] API
+and a simple, readable [source code][] that is a joy to [contribute][] to.
+
+For more information, check out the [official website][].
+
+We're happy to meet new contributors.
+If you want to **get involved** with Leaflet development, check out the [contribution guide][contribute].
+Let's make the best open-source library for maps that can possibly exist!
+
+ [Vladimir Agafonkin]: http://agafonkin.com/en
+ [contributors]: https://github.com/Leaflet/Leaflet/graphs/contributors
+ [features]: http://leafletjs.com/features.html
+ [plugins]: http://leafletjs.com/plugins.html
+ [well-documented]: http://leafletjs.com/reference.html "Leaflet API reference"
+ [source code]: https://github.com/Leaflet/Leaflet "Leaflet GitHub repository"
+ [hosted on GitHub]: http://github.com/Leaflet/Leaflet
+ [contribute]: https://github.com/Leaflet/Leaflet/blob/master/CONTRIBUTING.md "A guide to contributing to Leaflet"
+ [official website]: http://leafletjs.com
diff --git a/chimere/static/leaflet/images/layers.png b/chimere/static/leaflet/images/layers.png
new file mode 100644
index 0000000..ef90a08
Binary files /dev/null and b/chimere/static/leaflet/images/layers.png differ
diff --git a/chimere/static/leaflet/images/marker-icon.png b/chimere/static/leaflet/images/marker-icon.png
new file mode 100644
index 0000000..e2e9f75
Binary files /dev/null and b/chimere/static/leaflet/images/marker-icon.png differ
diff --git a/chimere/static/leaflet/images/marker-icon@2x.png b/chimere/static/leaflet/images/marker-icon@2x.png
new file mode 100644
index 0000000..0015b64
Binary files /dev/null and b/chimere/static/leaflet/images/marker-icon@2x.png differ
diff --git a/chimere/static/leaflet/images/marker-shadow.png b/chimere/static/leaflet/images/marker-shadow.png
new file mode 100644
index 0000000..d1e773c
Binary files /dev/null and b/chimere/static/leaflet/images/marker-shadow.png differ
diff --git a/chimere/static/leaflet/leaflet.css b/chimere/static/leaflet/leaflet.css
new file mode 100644
index 0000000..ea3da39
--- /dev/null
+++ b/chimere/static/leaflet/leaflet.css
@@ -0,0 +1,457 @@
+/* required styles */
+
+.leaflet-map-pane,
+.leaflet-tile,
+.leaflet-marker-icon,
+.leaflet-marker-shadow,
+.leaflet-tile-pane,
+.leaflet-overlay-pane,
+.leaflet-shadow-pane,
+.leaflet-marker-pane,
+.leaflet-popup-pane,
+.leaflet-overlay-pane svg,
+.leaflet-zoom-box,
+.leaflet-image-layer,
+.leaflet-layer {
+ position: absolute;
+ left: 0;
+ top: 0;
+ }
+.leaflet-container {
+ overflow: hidden;
+ -ms-touch-action: none;
+ }
+.leaflet-tile,
+.leaflet-marker-icon,
+.leaflet-marker-shadow {
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ user-select: none;
+ }
+.leaflet-marker-icon,
+.leaflet-marker-shadow {
+ display: block;
+ }
+/* map is broken in FF if you have max-width: 100% on tiles */
+.leaflet-container img {
+ max-width: none !important;
+ }
+/* stupid Android 2 doesn't understand "max-width: none" properly */
+.leaflet-container img.leaflet-image-layer {
+ max-width: 15000px !important;
+ }
+.leaflet-tile {
+ filter: inherit;
+ visibility: hidden;
+ }
+.leaflet-tile-loaded {
+ visibility: inherit;
+ }
+.leaflet-zoom-box {
+ width: 0;
+ height: 0;
+ }
+
+.leaflet-tile-pane { z-index: 2; }
+.leaflet-objects-pane { z-index: 3; }
+.leaflet-overlay-pane { z-index: 4; }
+.leaflet-shadow-pane { z-index: 5; }
+.leaflet-marker-pane { z-index: 6; }
+.leaflet-popup-pane { z-index: 7; }
+
+
+/* control positioning */
+
+.leaflet-control {
+ position: relative;
+ z-index: 7;
+ pointer-events: auto;
+ }
+.leaflet-top,
+.leaflet-bottom {
+ position: absolute;
+ z-index: 1000;
+ pointer-events: none;
+ }
+.leaflet-top {
+ top: 0;
+ }
+.leaflet-right {
+ right: 0;
+ }
+.leaflet-bottom {
+ bottom: 0;
+ }
+.leaflet-left {
+ left: 0;
+ }
+.leaflet-control {
+ float: left;
+ clear: both;
+ }
+.leaflet-right .leaflet-control {
+ float: right;
+ }
+.leaflet-top .leaflet-control {
+ margin-top: 10px;
+ }
+.leaflet-bottom .leaflet-control {
+ margin-bottom: 10px;
+ }
+.leaflet-left .leaflet-control {
+ margin-left: 10px;
+ }
+.leaflet-right .leaflet-control {
+ margin-right: 10px;
+ }
+
+
+/* zoom and fade animations */
+
+.leaflet-fade-anim .leaflet-tile,
+.leaflet-fade-anim .leaflet-popup {
+ opacity: 0;
+ -webkit-transition: opacity 0.2s linear;
+ -moz-transition: opacity 0.2s linear;
+ -o-transition: opacity 0.2s linear;
+ transition: opacity 0.2s linear;
+ }
+.leaflet-fade-anim .leaflet-tile-loaded,
+.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
+ opacity: 1;
+ }
+
+.leaflet-zoom-anim .leaflet-zoom-animated {
+ -webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);
+ -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1);
+ -o-transition: -o-transform 0.25s cubic-bezier(0,0,0.25,1);
+ transition: transform 0.25s cubic-bezier(0,0,0.25,1);
+ }
+.leaflet-zoom-anim .leaflet-tile,
+.leaflet-pan-anim .leaflet-tile,
+.leaflet-touching .leaflet-zoom-animated {
+ -webkit-transition: none;
+ -moz-transition: none;
+ -o-transition: none;
+ transition: none;
+ }
+
+.leaflet-zoom-anim .leaflet-zoom-hide {
+ visibility: hidden;
+ }
+
+
+/* cursors */
+
+.leaflet-clickable {
+ cursor: pointer;
+ }
+.leaflet-container {
+ cursor: -webkit-grab;
+ cursor: -moz-grab;
+ }
+.leaflet-popup-pane,
+.leaflet-control {
+ cursor: auto;
+ }
+.leaflet-dragging,
+.leaflet-dragging .leaflet-clickable,
+.leaflet-dragging .leaflet-container {
+ cursor: move;
+ cursor: -webkit-grabbing;
+ cursor: -moz-grabbing;
+ }
+
+
+/* visual tweaks */
+
+.leaflet-container {
+ background: #ddd;
+ outline: 0;
+ }
+.leaflet-container a {
+ color: #0078A8;
+ }
+.leaflet-container a.leaflet-active {
+ outline: 2px solid orange;
+ }
+.leaflet-zoom-box {
+ border: 2px dotted #05f;
+ background: white;
+ opacity: 0.5;
+ }
+
+
+/* general typography */
+.leaflet-container {
+ font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif;
+ }
+
+
+/* general toolbar styles */
+
+.leaflet-bar {
+ box-shadow: 0 0 8px rgba(0,0,0,0.4);
+ border: 1px solid #888;
+ -webkit-border-radius: 5px;
+ border-radius: 5px;
+ }
+.leaflet-bar-part {
+ background-color: rgba(255, 255, 255, 0.8);
+ border-bottom: 1px solid #aaa;
+ }
+.leaflet-bar-part-top {
+ -webkit-border-radius: 4px 4px 0 0;
+ border-radius: 4px 4px 0 0;
+ }
+.leaflet-bar-part-bottom {
+ -webkit-border-radius: 0 0 4px 4px;
+ border-radius: 0 0 4px 4px;
+ border-bottom: none;
+ }
+
+.leaflet-touch .leaflet-bar {
+ -webkit-border-radius: 10px;
+ border-radius: 10px;
+ }
+.leaflet-touch .leaflet-bar-part {
+ border-bottom: 4px solid rgba(0,0,0,0.3);
+ }
+.leaflet-touch .leaflet-bar-part-top {
+ -webkit-border-radius: 7px 7px 0 0;
+ border-radius: 7px 7px 0 0;
+ }
+.leaflet-touch .leaflet-bar-part-bottom {
+ -webkit-border-radius: 0 0 7px 7px;
+ border-radius: 0 0 7px 7px;
+ border-bottom: none;
+ }
+
+
+/* zoom control */
+
+.leaflet-container .leaflet-control-zoom {
+ margin-left: 13px;
+ margin-top: 12px;
+ }
+.leaflet-control-zoom a {
+ width: 22px;
+ height: 22px;
+ text-align: center;
+ text-decoration: none;
+ color: black;
+ }
+.leaflet-control-zoom a,
+.leaflet-control-layers-toggle {
+ background-position: 50% 50%;
+ background-repeat: no-repeat;
+ display: block;
+ }
+.leaflet-control-zoom a:hover {
+ background-color: #fff;
+ color: #777;
+ }
+.leaflet-control-zoom-in {
+ font: bold 18px/24px Arial, Helvetica, sans-serif;
+ }
+.leaflet-control-zoom-out {
+ font: bold 23px/20px Tahoma, Verdana, sans-serif;
+ }
+.leaflet-control-zoom a.leaflet-control-zoom-disabled {
+ cursor: default;
+ background-color: rgba(255, 255, 255, 0.8);
+ color: #bbb;
+ }
+
+.leaflet-touch .leaflet-control-zoom a {
+ width: 30px;
+ height: 30px;
+ }
+.leaflet-touch .leaflet-control-zoom-in {
+ font-size: 24px;
+ line-height: 29px;
+ }
+.leaflet-touch .leaflet-control-zoom-out {
+ font-size: 28px;
+ line-height: 24px;
+ }
+
+/* layers control */
+
+.leaflet-control-layers {
+ box-shadow: 0 1px 7px rgba(0,0,0,0.4);
+ background: #f8f8f9;
+ -webkit-border-radius: 8px;
+ border-radius: 8px;
+ }
+.leaflet-control-layers-toggle {
+ background-image: url(images/layers.png);
+ width: 36px;
+ height: 36px;
+ }
+.leaflet-touch .leaflet-control-layers-toggle {
+ width: 44px;
+ height: 44px;
+ }
+.leaflet-control-layers .leaflet-control-layers-list,
+.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
+ display: none;
+ }
+.leaflet-control-layers-expanded .leaflet-control-layers-list {
+ display: block;
+ position: relative;
+ }
+.leaflet-control-layers-expanded {
+ padding: 6px 10px 6px 6px;
+ color: #333;
+ background: #fff;
+ }
+.leaflet-control-layers-selector {
+ margin-top: 2px;
+ position: relative;
+ top: 1px;
+ }
+.leaflet-control-layers label {
+ display: block;
+ }
+.leaflet-control-layers-separator {
+ height: 0;
+ border-top: 1px solid #ddd;
+ margin: 5px -10px 5px -6px;
+ }
+
+
+/* attribution and scale controls */
+
+.leaflet-container .leaflet-control-attribution {
+ background-color: rgba(255, 255, 255, 0.7);
+ box-shadow: 0 0 5px #bbb;
+ margin: 0;
+ }
+.leaflet-control-attribution,
+.leaflet-control-scale-line {
+ padding: 0 5px;
+ color: #333;
+ }
+.leaflet-container .leaflet-control-attribution,
+.leaflet-container .leaflet-control-scale {
+ font-size: 11px;
+ }
+.leaflet-left .leaflet-control-scale {
+ margin-left: 5px;
+ }
+.leaflet-bottom .leaflet-control-scale {
+ margin-bottom: 5px;
+ }
+.leaflet-control-scale-line {
+ border: 2px solid #777;
+ border-top: none;
+ color: black;
+ line-height: 1.1;
+ padding: 2px 5px 1px;
+ font-size: 11px;
+ text-shadow: 1px 1px 1px #fff;
+ background-color: rgba(255, 255, 255, 0.5);
+ box-shadow: 0 -1px 5px rgba(0, 0, 0, 0.2);
+ white-space: nowrap;
+ overflow: hidden;
+ }
+.leaflet-control-scale-line:not(:first-child) {
+ border-top: 2px solid #777;
+ border-bottom: none;
+ margin-top: -2px;
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
+ }
+.leaflet-control-scale-line:not(:first-child):not(:last-child) {
+ border-bottom: 2px solid #777;
+ }
+
+.leaflet-touch .leaflet-control-attribution,
+.leaflet-touch .leaflet-control-layers,
+.leaflet-touch .leaflet-control-zoom {
+ box-shadow: none;
+ }
+.leaflet-touch .leaflet-control-layers,
+.leaflet-touch .leaflet-control-zoom {
+ border: 4px solid rgba(0,0,0,0.3);
+ }
+
+
+/* popup */
+
+.leaflet-popup {
+ position: absolute;
+ text-align: center;
+ }
+.leaflet-popup-content-wrapper {
+ padding: 1px;
+ text-align: left;
+ -webkit-border-radius: 20px;
+ border-radius: 20px;
+ }
+.leaflet-popup-content {
+ margin: 14px 20px;
+ line-height: 1.4;
+ }
+.leaflet-popup-content p {
+ margin: 18px 0;
+ }
+.leaflet-popup-tip-container {
+ margin: 0 auto;
+ width: 40px;
+ height: 20px;
+ position: relative;
+ overflow: hidden;
+ }
+.leaflet-popup-tip {
+ width: 15px;
+ height: 15px;
+ padding: 1px;
+
+ margin: -8px auto 0;
+
+ -webkit-transform: rotate(45deg);
+ -moz-transform: rotate(45deg);
+ -ms-transform: rotate(45deg);
+ -o-transform: rotate(45deg);
+ transform: rotate(45deg);
+ }
+.leaflet-popup-content-wrapper, .leaflet-popup-tip {
+ background: white;
+
+ box-shadow: 0 3px 14px rgba(0,0,0,0.4);
+ }
+.leaflet-container a.leaflet-popup-close-button {
+ position: absolute;
+ top: 0;
+ right: 0;
+ padding: 4px 5px 0 0;
+ text-align: center;
+ width: 18px;
+ height: 14px;
+ font: 16px/14px Tahoma, Verdana, sans-serif;
+ color: #c3c3c3;
+ text-decoration: none;
+ font-weight: bold;
+ background: transparent;
+ }
+.leaflet-container a.leaflet-popup-close-button:hover {
+ color: #999;
+ }
+.leaflet-popup-scrolled {
+ overflow: auto;
+ border-bottom: 1px solid #ddd;
+ border-top: 1px solid #ddd;
+ }
+
+
+/* div icon */
+
+.leaflet-div-icon {
+ background: #fff;
+ border: 1px solid #666;
+ }
+.leaflet-editing-icon {
+ -webkit-border-radius: 2px;
+ border-radius: 2px;
+ }
diff --git a/chimere/static/leaflet/leaflet.ie.css b/chimere/static/leaflet/leaflet.ie.css
new file mode 100644
index 0000000..b59c814
--- /dev/null
+++ b/chimere/static/leaflet/leaflet.ie.css
@@ -0,0 +1,57 @@
+.leaflet-vml-shape {
+ width: 1px;
+ height: 1px;
+ }
+.lvml {
+ behavior: url(#default#VML);
+ display: inline-block;
+ position: absolute;
+ }
+
+.leaflet-control {
+ display: inline;
+ }
+
+.leaflet-popup-tip {
+ width: 21px;
+ _width: 27px;
+ margin: 0 auto;
+ _margin-top: -3px;
+
+ filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
+ -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
+ }
+.leaflet-popup-tip-container {
+ margin-top: -1px;
+ }
+.leaflet-popup-content-wrapper, .leaflet-popup-tip {
+ border: 1px solid #999;
+ }
+.leaflet-popup-content-wrapper {
+ zoom: 1;
+ }
+
+.leaflet-control-zoom,
+.leaflet-control-layers {
+ border: 3px solid #999;
+ }
+.leaflet-control-zoom a {
+ background-color: #eee;
+ }
+.leaflet-control-zoom a:hover {
+ background-color: #fff;
+ }
+.leaflet-control-layers-toggle {
+ }
+.leaflet-control-attribution,
+.leaflet-control-layers,
+.leaflet-control-scale-line {
+ background: white;
+ }
+.leaflet-zoom-box {
+ filter: alpha(opacity=50);
+ }
+.leaflet-control-attribution {
+ border-top: 1px solid #bbb;
+ border-left: 1px solid #bbb;
+ }
diff --git a/chimere/static/leaflet/leaflet.js b/chimere/static/leaflet/leaflet.js
new file mode 100644
index 0000000..bdf0b69
--- /dev/null
+++ b/chimere/static/leaflet/leaflet.js
@@ -0,0 +1,8 @@
+/*
+ Leaflet, a JavaScript library for mobile-friendly interactive maps. http://leafletjs.com
+ (c) 2010-2013, Vladimir Agafonkin, CloudMade
+*/
+(function(t,e,i){var n,o;typeof exports!=i+""?n=exports:(o=t.L,n={},n.noConflict=function(){return t.L=o,this},t.L=n),n.version="0.5.1",n.Util={extend:function(t){var e,i,n,o,s=Array.prototype.slice.call(arguments,1);for(i=0,n=s.length;n>i;i++){o=s[i]||{};for(e in o)o.hasOwnProperty(e)&&(t[e]=o[e])}return t},bind:function(t,e){var i=arguments.length>2?Array.prototype.slice.call(arguments,2):null;return function(){return t.apply(e,i||arguments)}},stamp:function(){var t=0,e="_leaflet_id";return function(i){return i[e]=i[e]||++t,i[e]}}(),limitExecByInterval:function(t,e,n){var o,s;return function a(){var r=arguments;return o?(s=!0,i):(o=!0,setTimeout(function(){o=!1,s&&(a.apply(n,r),s=!1)},e),t.apply(n,r),i)}},falseFn:function(){return!1},formatNum:function(t,e){var i=Math.pow(10,e||5);return Math.round(t*i)/i},splitWords:function(t){return t.replace(/^\s+|\s+$/g,"").split(/\s+/)},setOptions:function(t,e){return t.options=n.extend({},t.options,e),t.options},getParamString:function(t,e){var i=[];for(var n in t)t.hasOwnProperty(n)&&i.push(n+"="+t[n]);return(e&&-1!==e.indexOf("?")?"&":"?")+i.join("&")},template:function(t,e){return t.replace(/\{ *([\w_]+) *\}/g,function(t,i){var n=e[i];if(!e.hasOwnProperty(i))throw Error("No value provided for variable "+t);return n})},isArray:function(t){return"[object Array]"===Object.prototype.toString.call(t)},emptyImageUrl:"data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs="},function(){function e(e){var i,n,o=["webkit","moz","o","ms"];for(i=0;o.length>i&&!n;i++)n=t[o[i]+e];return n}function o(e){var i=+new Date,n=Math.max(0,16-(i-s));return s=i+n,t.setTimeout(e,n)}var s=0,a=t.requestAnimationFrame||e("RequestAnimationFrame")||o,r=t.cancelAnimationFrame||e("CancelAnimationFrame")||e("CancelRequestAnimationFrame")||function(e){t.clearTimeout(e)};n.Util.requestAnimFrame=function(e,s,r,h){return e=n.bind(e,s),r&&a===o?(e(),i):a.call(t,e,h)},n.Util.cancelAnimFrame=function(e){e&&r.call(t,e)}}(),n.extend=n.Util.extend,n.bind=n.Util.bind,n.stamp=n.Util.stamp,n.setOptions=n.Util.setOptions,n.Class=function(){},n.Class.extend=function(t){var e=function(){this.initialize&&this.initialize.apply(this,arguments),this._initHooks&&this.callInitHooks()},i=function(){};i.prototype=this.prototype;var o=new i;o.constructor=e,e.prototype=o;for(var s in this)this.hasOwnProperty(s)&&"prototype"!==s&&(e[s]=this[s]);t.statics&&(n.extend(e,t.statics),delete t.statics),t.includes&&(n.Util.extend.apply(null,[o].concat(t.includes)),delete t.includes),t.options&&o.options&&(t.options=n.extend({},o.options,t.options)),n.extend(o,t),o._initHooks=[];var a=this;return o.callInitHooks=function(){if(!this._initHooksCalled){a.prototype.callInitHooks&&a.prototype.callInitHooks.call(this),this._initHooksCalled=!0;for(var t=0,e=o._initHooks.length;e>t;t++)o._initHooks[t].call(this)}},e},n.Class.include=function(t){n.extend(this.prototype,t)},n.Class.mergeOptions=function(t){n.extend(this.prototype.options,t)},n.Class.addInitHook=function(t){var e=Array.prototype.slice.call(arguments,1),i="function"==typeof t?t:function(){this[t].apply(this,e)};this.prototype._initHooks=this.prototype._initHooks||[],this.prototype._initHooks.push(i)};var s="_leaflet_events";n.Mixin={},n.Mixin.Events={addEventListener:function(t,e,i){var o,a,r,h=this[s]=this[s]||{};if("object"==typeof t){for(o in t)t.hasOwnProperty(o)&&this.addEventListener(o,t[o],e);return this}for(t=n.Util.splitWords(t),a=0,r=t.length;r>a;a++)h[t[a]]=h[t[a]]||[],h[t[a]].push({action:e,context:i||this});return this},hasEventListeners:function(t){return s in this&&t in this[s]&&this[s][t].length>0},removeEventListener:function(t,e,i){var o,a,r,h,l,u=this[s];if("object"==typeof t){for(o in t)t.hasOwnProperty(o)&&this.removeEventListener(o,t[o],e);return this}for(t=n.Util.splitWords(t),a=0,r=t.length;r>a;a++)if(this.hasEventListeners(t[a]))for(h=u[t[a]],l=h.length-1;l>=0;l--)e&&h[l].action!==e||i&&h[l].context!==i||h.splice(l,1);return this},fireEvent:function(t,e){if(!this.hasEventListeners(t))return this;for(var i=n.extend({type:t,target:this},e),o=this[s][t].slice(),a=0,r=o.length;r>a;a++)o[a].action.call(o[a].context||this,i);return this}},n.Mixin.Events.on=n.Mixin.Events.addEventListener,n.Mixin.Events.off=n.Mixin.Events.removeEventListener,n.Mixin.Events.fire=n.Mixin.Events.fireEvent,function(){var o=!!t.ActiveXObject,s=o&&!t.XMLHttpRequest,a=o&&!e.querySelector,r=navigator.userAgent.toLowerCase(),h=-1!==r.indexOf("webkit"),l=-1!==r.indexOf("chrome"),u=-1!==r.indexOf("android"),c=-1!==r.search("android [23]"),_=typeof orientation!=i+"",d=t.navigator&&t.navigator.msPointerEnabled&&t.navigator.msMaxTouchPoints,p="devicePixelRatio"in t&&t.devicePixelRatio>1||"matchMedia"in t&&t.matchMedia("(min-resolution:144dpi)")&&t.matchMedia("(min-resolution:144dpi)").matches,m=e.documentElement,f=o&&"transition"in m.style,g="WebKitCSSMatrix"in t&&"m11"in new t.WebKitCSSMatrix,v="MozPerspective"in m.style,y="OTransition"in m.style,L=!t.L_DISABLE_3D&&(f||g||v||y),P=!t.L_NO_TOUCH&&function(){var t="ontouchstart";if(d||t in m)return!0;var i=e.createElement("div"),n=!1;return i.setAttribute?(i.setAttribute(t,"return;"),"function"==typeof i[t]&&(n=!0),i.removeAttribute(t),i=null,n):!1}();n.Browser={ie:o,ie6:s,ie7:a,webkit:h,android:u,android23:c,chrome:l,ie3d:f,webkit3d:g,gecko3d:v,opera3d:y,any3d:L,mobile:_,mobileWebkit:_&&h,mobileWebkit3d:_&&g,mobileOpera:_&&t.opera,touch:P,msTouch:d,retina:p}}(),n.Point=function(t,e,i){this.x=i?Math.round(t):t,this.y=i?Math.round(e):e},n.Point.prototype={clone:function(){return new n.Point(this.x,this.y)},add:function(t){return this.clone()._add(n.point(t))},_add:function(t){return this.x+=t.x,this.y+=t.y,this},subtract:function(t){return this.clone()._subtract(n.point(t))},_subtract:function(t){return this.x-=t.x,this.y-=t.y,this},divideBy:function(t){return this.clone()._divideBy(t)},_divideBy:function(t){return this.x/=t,this.y/=t,this},multiplyBy:function(t){return this.clone()._multiplyBy(t)},_multiplyBy:function(t){return this.x*=t,this.y*=t,this},round:function(){return this.clone()._round()},_round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this},floor:function(){return this.clone()._floor()},_floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this},distanceTo:function(t){t=n.point(t);var e=t.x-this.x,i=t.y-this.y;return Math.sqrt(e*e+i*i)},equals:function(t){return t.x===this.x&&t.y===this.y},toString:function(){return"Point("+n.Util.formatNum(this.x)+", "+n.Util.formatNum(this.y)+")"}},n.point=function(t,e,i){return t instanceof n.Point?t:n.Util.isArray(t)?new n.Point(t[0],t[1]):isNaN(t)?t:new n.Point(t,e,i)},n.Bounds=function(t,e){if(t)for(var i=e?[t,e]:t,n=0,o=i.length;o>n;n++)this.extend(i[n])},n.Bounds.prototype={extend:function(t){return t=n.point(t),this.min||this.max?(this.min.x=Math.min(t.x,this.min.x),this.max.x=Math.max(t.x,this.max.x),this.min.y=Math.min(t.y,this.min.y),this.max.y=Math.max(t.y,this.max.y)):(this.min=t.clone(),this.max=t.clone()),this},getCenter:function(t){return new n.Point((this.min.x+this.max.x)/2,(this.min.y+this.max.y)/2,t)},getBottomLeft:function(){return new n.Point(this.min.x,this.max.y)},getTopRight:function(){return new n.Point(this.max.x,this.min.y)},getSize:function(){return this.max.subtract(this.min)},contains:function(t){var e,i;return t="number"==typeof t[0]||t instanceof n.Point?n.point(t):n.bounds(t),t instanceof n.Bounds?(e=t.min,i=t.max):e=i=t,e.x>=this.min.x&&i.x<=this.max.x&&e.y>=this.min.y&&i.y<=this.max.y},intersects:function(t){t=n.bounds(t);var e=this.min,i=this.max,o=t.min,s=t.max,a=s.x>=e.x&&o.x<=i.x,r=s.y>=e.y&&o.y<=i.y;return a&&r},isValid:function(){return!(!this.min||!this.max)}},n.bounds=function(t,e){return!t||t instanceof n.Bounds?t:new n.Bounds(t,e)},n.Transformation=function(t,e,i,n){this._a=t,this._b=e,this._c=i,this._d=n},n.Transformation.prototype={transform:function(t,e){return this._transform(t.clone(),e)},_transform:function(t,e){return e=e||1,t.x=e*(this._a*t.x+this._b),t.y=e*(this._c*t.y+this._d),t},untransform:function(t,e){return e=e||1,new n.Point((t.x/e-this._b)/this._a,(t.y/e-this._d)/this._c)}},n.DomUtil={get:function(t){return"string"==typeof t?e.getElementById(t):t},getStyle:function(t,i){var n=t.style[i];if(!n&&t.currentStyle&&(n=t.currentStyle[i]),(!n||"auto"===n)&&e.defaultView){var o=e.defaultView.getComputedStyle(t,null);n=o?o[i]:null}return"auto"===n?null:n},getViewportOffset:function(t){var i,o=0,s=0,a=t,r=e.body,h=n.Browser.ie7;do{if(o+=a.offsetTop||0,s+=a.offsetLeft||0,o+=parseInt(n.DomUtil.getStyle(a,"borderTopWidth"),10)||0,s+=parseInt(n.DomUtil.getStyle(a,"borderLeftWidth"),10)||0,i=n.DomUtil.getStyle(a,"position"),a.offsetParent===r&&"absolute"===i)break;if("fixed"===i){o+=r.scrollTop||0,s+=r.scrollLeft||0;break}a=a.offsetParent}while(a);a=t;do{if(a===r)break;o-=a.scrollTop||0,s-=a.scrollLeft||0,n.DomUtil.documentIsLtr()||!n.Browser.webkit&&!h||(s+=a.scrollWidth-a.clientWidth,h&&"hidden"!==n.DomUtil.getStyle(a,"overflow-y")&&"hidden"!==n.DomUtil.getStyle(a,"overflow")&&(s+=17)),a=a.parentNode}while(a);return new n.Point(s,o)},documentIsLtr:function(){return n.DomUtil._docIsLtrCached||(n.DomUtil._docIsLtrCached=!0,n.DomUtil._docIsLtr="ltr"===n.DomUtil.getStyle(e.body,"direction")),n.DomUtil._docIsLtr},create:function(t,i,n){var o=e.createElement(t);return o.className=i,n&&n.appendChild(o),o},disableTextSelection:function(){e.selection&&e.selection.empty&&e.selection.empty(),this._onselectstart||(this._onselectstart=e.onselectstart||null,e.onselectstart=n.Util.falseFn)},enableTextSelection:function(){e.onselectstart===n.Util.falseFn&&(e.onselectstart=this._onselectstart,this._onselectstart=null)},hasClass:function(t,e){return t.className.length>0&&RegExp("(^|\\s)"+e+"(\\s|$)").test(t.className)},addClass:function(t,e){n.DomUtil.hasClass(t,e)||(t.className+=(t.className?" ":"")+e)},removeClass:function(t,e){function i(t,i){return i===e?"":t}t.className=t.className.replace(/(\S+)\s*/g,i).replace(/(^\s+|\s+$)/,"")},setOpacity:function(t,e){if("opacity"in t.style)t.style.opacity=e;else if("filter"in t.style){var i=!1,n="DXImageTransform.Microsoft.Alpha";try{i=t.filters.item(n)}catch(o){}e=Math.round(100*e),i?(i.Enabled=100!==e,i.Opacity=e):t.style.filter+=" progid:"+n+"(opacity="+e+")"}},testProp:function(t){for(var i=e.documentElement.style,n=0;t.length>n;n++)if(t[n]in i)return t[n];return!1},getTranslateString:function(t){var e=n.Browser.webkit3d,i="translate"+(e?"3d":"")+"(",o=(e?",0":"")+")";return i+t.x+"px,"+t.y+"px"+o},getScaleString:function(t,e){var i=n.DomUtil.getTranslateString(e.add(e.multiplyBy(-1*t))),o=" scale("+t+") ";return i+o},setPosition:function(t,e,i){t._leaflet_pos=e,!i&&n.Browser.any3d?(t.style[n.DomUtil.TRANSFORM]=n.DomUtil.getTranslateString(e),n.Browser.mobileWebkit3d&&(t.style.WebkitBackfaceVisibility="hidden")):(t.style.left=e.x+"px",t.style.top=e.y+"px")},getPosition:function(t){return t._leaflet_pos}},n.DomUtil.TRANSFORM=n.DomUtil.testProp(["transform","WebkitTransform","OTransform","MozTransform","msTransform"]),n.DomUtil.TRANSITION=n.DomUtil.testProp(["webkitTransition","transition","OTransition","MozTransition","msTransition"]),n.DomUtil.TRANSITION_END="webkitTransition"===n.DomUtil.TRANSITION||"OTransition"===n.DomUtil.TRANSITION?n.DomUtil.TRANSITION+"End":"transitionend",n.LatLng=function(t,e){var i=parseFloat(t),n=parseFloat(e);if(isNaN(i)||isNaN(n))throw Error("Invalid LatLng object: ("+t+", "+e+")");this.lat=i,this.lng=n},n.extend(n.LatLng,{DEG_TO_RAD:Math.PI/180,RAD_TO_DEG:180/Math.PI,MAX_MARGIN:1e-9}),n.LatLng.prototype={equals:function(t){if(!t)return!1;t=n.latLng(t);var e=Math.max(Math.abs(this.lat-t.lat),Math.abs(this.lng-t.lng));return n.LatLng.MAX_MARGIN>=e},toString:function(t){return"LatLng("+n.Util.formatNum(this.lat,t)+", "+n.Util.formatNum(this.lng,t)+")"},distanceTo:function(t){t=n.latLng(t);var e=6378137,i=n.LatLng.DEG_TO_RAD,o=(t.lat-this.lat)*i,s=(t.lng-this.lng)*i,a=this.lat*i,r=t.lat*i,h=Math.sin(o/2),l=Math.sin(s/2),u=h*h+l*l*Math.cos(a)*Math.cos(r);return 2*e*Math.atan2(Math.sqrt(u),Math.sqrt(1-u))},wrap:function(t,e){var i=this.lng;return t=t||-180,e=e||180,i=(i+e)%(e-t)+(t>i||i===e?e:t),new n.LatLng(this.lat,i)}},n.latLng=function(t,e){return t instanceof n.LatLng?t:n.Util.isArray(t)?new n.LatLng(t[0],t[1]):isNaN(t)?t:new n.LatLng(t,e)},n.LatLngBounds=function(t,e){if(t)for(var i=e?[t,e]:t,n=0,o=i.length;o>n;n++)this.extend(i[n])},n.LatLngBounds.prototype={extend:function(t){return t="number"==typeof t[0]||"string"==typeof t[0]||t instanceof n.LatLng?n.latLng(t):n.latLngBounds(t),t instanceof n.LatLng?this._southWest||this._northEast?(this._southWest.lat=Math.min(t.lat,this._southWest.lat),this._southWest.lng=Math.min(t.lng,this._southWest.lng),this._northEast.lat=Math.max(t.lat,this._northEast.lat),this._northEast.lng=Math.max(t.lng,this._northEast.lng)):(this._southWest=new n.LatLng(t.lat,t.lng),this._northEast=new n.LatLng(t.lat,t.lng)):t instanceof n.LatLngBounds&&(this.extend(t._southWest),this.extend(t._northEast)),this},pad:function(t){var e=this._southWest,i=this._northEast,o=Math.abs(e.lat-i.lat)*t,s=Math.abs(e.lng-i.lng)*t;return new n.LatLngBounds(new n.LatLng(e.lat-o,e.lng-s),new n.LatLng(i.lat+o,i.lng+s))},getCenter:function(){return new n.LatLng((this._southWest.lat+this._northEast.lat)/2,(this._southWest.lng+this._northEast.lng)/2)},getSouthWest:function(){return this._southWest},getNorthEast:function(){return this._northEast},getNorthWest:function(){return new n.LatLng(this._northEast.lat,this._southWest.lng)},getSouthEast:function(){return new n.LatLng(this._southWest.lat,this._northEast.lng)},contains:function(t){t="number"==typeof t[0]||t instanceof n.LatLng?n.latLng(t):n.latLngBounds(t);var e,i,o=this._southWest,s=this._northEast;return t instanceof n.LatLngBounds?(e=t.getSouthWest(),i=t.getNorthEast()):e=i=t,e.lat>=o.lat&&i.lat<=s.lat&&e.lng>=o.lng&&i.lng<=s.lng},intersects:function(t){t=n.latLngBounds(t);var e=this._southWest,i=this._northEast,o=t.getSouthWest(),s=t.getNorthEast(),a=s.lat>=e.lat&&o.lat<=i.lat,r=s.lng>=e.lng&&o.lng<=i.lng;return a&&r},toBBoxString:function(){var t=this._southWest,e=this._northEast;return[t.lng,t.lat,e.lng,e.lat].join(",")},equals:function(t){return t?(t=n.latLngBounds(t),this._southWest.equals(t.getSouthWest())&&this._northEast.equals(t.getNorthEast())):!1},isValid:function(){return!(!this._southWest||!this._northEast)}},n.latLngBounds=function(t,e){return!t||t instanceof n.LatLngBounds?t:new n.LatLngBounds(t,e)},n.Projection={},n.Projection.SphericalMercator={MAX_LATITUDE:85.0511287798,project:function(t){var e=n.LatLng.DEG_TO_RAD,i=this.MAX_LATITUDE,o=Math.max(Math.min(i,t.lat),-i),s=t.lng*e,a=o*e;return a=Math.log(Math.tan(Math.PI/4+a/2)),new n.Point(s,a)},unproject:function(t){var e=n.LatLng.RAD_TO_DEG,i=t.x*e,o=(2*Math.atan(Math.exp(t.y))-Math.PI/2)*e;return new n.LatLng(o,i)}},n.Projection.LonLat={project:function(t){return new n.Point(t.lng,t.lat)},unproject:function(t){return new n.LatLng(t.y,t.x)}},n.CRS={latLngToPoint:function(t,e){var i=this.projection.project(t),n=this.scale(e);return this.transformation._transform(i,n)},pointToLatLng:function(t,e){var i=this.scale(e),n=this.transformation.untransform(t,i);return this.projection.unproject(n)},project:function(t){return this.projection.project(t)},scale:function(t){return 256*Math.pow(2,t)}},n.CRS.Simple=n.extend({},n.CRS,{projection:n.Projection.LonLat,transformation:new n.Transformation(1,0,-1,0),scale:function(t){return Math.pow(2,t)}}),n.CRS.EPSG3857=n.extend({},n.CRS,{code:"EPSG:3857",projection:n.Projection.SphericalMercator,transformation:new n.Transformation(.5/Math.PI,.5,-.5/Math.PI,.5),project:function(t){var e=this.projection.project(t),i=6378137;return e.multiplyBy(i)}}),n.CRS.EPSG900913=n.extend({},n.CRS.EPSG3857,{code:"EPSG:900913"}),n.CRS.EPSG4326=n.extend({},n.CRS,{code:"EPSG:4326",projection:n.Projection.LonLat,transformation:new n.Transformation(1/360,.5,-1/360,.5)}),n.Map=n.Class.extend({includes:n.Mixin.Events,options:{crs:n.CRS.EPSG3857,fadeAnimation:n.DomUtil.TRANSITION&&!n.Browser.android23,trackResize:!0,markerZoomAnimation:n.DomUtil.TRANSITION&&n.Browser.any3d},initialize:function(t,e){e=n.setOptions(this,e),this._initContainer(t),this._initLayout(),this.callInitHooks(),this._initEvents(),e.maxBounds&&this.setMaxBounds(e.maxBounds),e.center&&e.zoom!==i&&this.setView(n.latLng(e.center),e.zoom,!0),this._initLayers(e.layers)},setView:function(t,e){return this._resetView(n.latLng(t),this._limitZoom(e)),this},setZoom:function(t){return this.setView(this.getCenter(),t)},zoomIn:function(t){return this.setZoom(this._zoom+(t||1))},zoomOut:function(t){return this.setZoom(this._zoom-(t||1))},fitBounds:function(t){var e=this.getBoundsZoom(t);return this.setView(n.latLngBounds(t).getCenter(),e)},fitWorld:function(){var t=new n.LatLng(-60,-170),e=new n.LatLng(85,179);return this.fitBounds(new n.LatLngBounds(t,e))},panTo:function(t){return this.setView(t,this._zoom)},panBy:function(t){return this.fire("movestart"),this._rawPanBy(n.point(t)),this.fire("move"),this.fire("moveend")},setMaxBounds:function(t){if(t=n.latLngBounds(t),this.options.maxBounds=t,!t)return this._boundsMinZoom=null,this;var e=this.getBoundsZoom(t,!0);return this._boundsMinZoom=e,this._loaded&&(e>this._zoom?this.setView(t.getCenter(),e):this.panInsideBounds(t)),this},panInsideBounds:function(t){t=n.latLngBounds(t);var e=this.getBounds(),i=this.project(e.getSouthWest()),o=this.project(e.getNorthEast()),s=this.project(t.getSouthWest()),a=this.project(t.getNorthEast()),r=0,h=0;return o.ya.x&&(r=a.x-o.x),i.y>s.y&&(h=s.y-i.y),i.x=r);return c&&e?null:e?r:r-1},getSize:function(){return(!this._size||this._sizeChanged)&&(this._size=new n.Point(this._container.clientWidth,this._container.clientHeight),this._sizeChanged=!1),this._size.clone()},getPixelBounds:function(){var t=this._getTopLeftPoint();return new n.Bounds(t,t.add(this.getSize()))},getPixelOrigin:function(){return this._initialTopLeftPoint},getPanes:function(){return this._panes},getContainer:function(){return this._container},getZoomScale:function(t){var e=this.options.crs;return e.scale(t)/e.scale(this._zoom)},getScaleZoom:function(t){return this._zoom+Math.log(t)/Math.LN2},project:function(t,e){return e=e===i?this._zoom:e,this.options.crs.latLngToPoint(n.latLng(t),e)},unproject:function(t,e){return e=e===i?this._zoom:e,this.options.crs.pointToLatLng(n.point(t),e)},layerPointToLatLng:function(t){var e=n.point(t).add(this._initialTopLeftPoint);return this.unproject(e)},latLngToLayerPoint:function(t){var e=this.project(n.latLng(t))._round();return e._subtract(this._initialTopLeftPoint)},containerPointToLayerPoint:function(t){return n.point(t).subtract(this._getMapPanePos())},layerPointToContainerPoint:function(t){return n.point(t).add(this._getMapPanePos())},containerPointToLatLng:function(t){var e=this.containerPointToLayerPoint(n.point(t));return this.layerPointToLatLng(e)},latLngToContainerPoint:function(t){return this.layerPointToContainerPoint(this.latLngToLayerPoint(n.latLng(t)))},mouseEventToContainerPoint:function(t){return n.DomEvent.getMousePosition(t,this._container)},mouseEventToLayerPoint:function(t){return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(t))},mouseEventToLatLng:function(t){return this.layerPointToLatLng(this.mouseEventToLayerPoint(t))},_initContainer:function(t){var e=this._container=n.DomUtil.get(t);if(e._leaflet)throw Error("Map container is already initialized.");e._leaflet=!0},_initLayout:function(){var t=this._container;n.DomUtil.addClass(t,"leaflet-container"),n.Browser.touch&&n.DomUtil.addClass(t,"leaflet-touch"),this.options.fadeAnimation&&n.DomUtil.addClass(t,"leaflet-fade-anim");var e=n.DomUtil.getStyle(t,"position");"absolute"!==e&&"relative"!==e&&"fixed"!==e&&(t.style.position="relative"),this._initPanes(),this._initControlPos&&this._initControlPos()},_initPanes:function(){var t=this._panes={};this._mapPane=t.mapPane=this._createPane("leaflet-map-pane",this._container),this._tilePane=t.tilePane=this._createPane("leaflet-tile-pane",this._mapPane),t.objectsPane=this._createPane("leaflet-objects-pane",this._mapPane),t.shadowPane=this._createPane("leaflet-shadow-pane"),t.overlayPane=this._createPane("leaflet-overlay-pane"),t.markerPane=this._createPane("leaflet-marker-pane"),t.popupPane=this._createPane("leaflet-popup-pane");var e=" leaflet-zoom-hide";this.options.markerZoomAnimation||(n.DomUtil.addClass(t.markerPane,e),n.DomUtil.addClass(t.shadowPane,e),n.DomUtil.addClass(t.popupPane,e))},_createPane:function(t,e){return n.DomUtil.create("div",t,e||this._panes.objectsPane)},_initLayers:function(t){t=t?n.Util.isArray(t)?t:[t]:[],this._layers={},this._zoomBoundLayers={},this._tileLayersNum=0;var e,i;for(e=0,i=t.length;i>e;e++)this.addLayer(t[e])},_resetView:function(t,e,i,o){var s=this._zoom!==e;o||(this.fire("movestart"),s&&this.fire("zoomstart")),this._zoom=e,this._initialTopLeftPoint=this._getNewTopLeftPoint(t),i?this._initialTopLeftPoint._add(this._getMapPanePos()):n.DomUtil.setPosition(this._mapPane,new n.Point(0,0)),this._tileLayersToLoad=this._tileLayersNum;var a=!this._loaded;this._loaded=!0,this.fire("viewreset",{hard:!i}),this.fire("move"),(s||o)&&this.fire("zoomend"),this.fire("moveend",{hard:!i}),a&&this.fire("load")},_rawPanBy:function(t){n.DomUtil.setPosition(this._mapPane,this._getMapPanePos().subtract(t))},_updateZoomLevels:function(){var t,e=1/0,n=-1/0;for(t in this._zoomBoundLayers)if(this._zoomBoundLayers.hasOwnProperty(t)){var o=this._zoomBoundLayers[t];isNaN(o.options.minZoom)||(e=Math.min(e,o.options.minZoom)),isNaN(o.options.maxZoom)||(n=Math.max(n,o.options.maxZoom))}t===i?this._layersMaxZoom=this._layersMinZoom=i:(this._layersMaxZoom=n,this._layersMinZoom=e)},_initEvents:function(){if(n.DomEvent){n.DomEvent.on(this._container,"click",this._onMouseClick,this);var e,i,o=["dblclick","mousedown","mouseup","mouseenter","mouseleave","mousemove","contextmenu"];for(e=0,i=o.length;i>e;e++)n.DomEvent.on(this._container,o[e],this._fireMouseEvent,this);this.options.trackResize&&n.DomEvent.on(t,"resize",this._onResize,this)}},_onResize:function(){n.Util.cancelAnimFrame(this._resizeRequest),this._resizeRequest=n.Util.requestAnimFrame(this.invalidateSize,this,!1,this._container)},_onMouseClick:function(t){!this._loaded||this.dragging&&this.dragging.moved()||(this.fire("preclick"),this._fireMouseEvent(t))},_fireMouseEvent:function(t){if(this._loaded){var e=t.type;if(e="mouseenter"===e?"mouseover":"mouseleave"===e?"mouseout":e,this.hasEventListeners(e)){"contextmenu"===e&&n.DomEvent.preventDefault(t);var i=this.mouseEventToContainerPoint(t),o=this.containerPointToLayerPoint(i),s=this.layerPointToLatLng(o);this.fire(e,{latlng:s,layerPoint:o,containerPoint:i,originalEvent:t})}}},_onTileLayerLoad:function(){this._tileLayersToLoad--,this._tileLayersNum&&!this._tileLayersToLoad&&this._tileBg&&(clearTimeout(this._clearTileBgTimer),this._clearTileBgTimer=setTimeout(n.bind(this._clearTileBg,this),500))},whenReady:function(t,e){return this._loaded?t.call(e||this,this):this.on("load",t,e),this},_getMapPanePos:function(){return n.DomUtil.getPosition(this._mapPane)},_getTopLeftPoint:function(){if(!this._loaded)throw Error("Set map center and zoom first.");return this._initialTopLeftPoint.subtract(this._getMapPanePos())},_getNewTopLeftPoint:function(t,e){var i=this.getSize()._divideBy(2);return this.project(t,e)._subtract(i)._round()},_latLngToNewLayerPoint:function(t,e,i){var n=this._getNewTopLeftPoint(i,e).add(this._getMapPanePos());return this.project(t,e)._subtract(n)},_getCenterLayerPoint:function(){return this.containerPointToLayerPoint(this.getSize()._divideBy(2))},_getCenterOffset:function(t){return this.latLngToLayerPoint(t).subtract(this._getCenterLayerPoint())},_limitZoom:function(t){var e=this.getMinZoom(),i=this.getMaxZoom();return Math.max(e,Math.min(i,t))}}),n.map=function(t,e){return new n.Map(t,e)},n.Projection.Mercator={MAX_LATITUDE:85.0840591556,R_MINOR:6356752.3142,R_MAJOR:6378137,project:function(t){var e=n.LatLng.DEG_TO_RAD,i=this.MAX_LATITUDE,o=Math.max(Math.min(i,t.lat),-i),s=this.R_MAJOR,a=this.R_MINOR,r=t.lng*e*s,h=o*e,l=a/s,u=Math.sqrt(1-l*l),c=u*Math.sin(h);c=Math.pow((1-c)/(1+c),.5*u);var _=Math.tan(.5*(.5*Math.PI-h))/c;return h=-a*Math.log(_),new n.Point(r,h)},unproject:function(t){for(var e,i=n.LatLng.RAD_TO_DEG,o=this.R_MAJOR,s=this.R_MINOR,a=t.x*i/o,r=s/o,h=Math.sqrt(1-r*r),l=Math.exp(-t.y/s),u=Math.PI/2-2*Math.atan(l),c=15,_=1e-7,d=c,p=.1;Math.abs(p)>_&&--d>0;)e=h*Math.sin(u),p=Math.PI/2-2*Math.atan(l*Math.pow((1-e)/(1+e),.5*h))-u,u+=p;return new n.LatLng(u*i,a)}},n.CRS.EPSG3395=n.extend({},n.CRS,{code:"EPSG:3395",projection:n.Projection.Mercator,transformation:function(){var t=n.Projection.Mercator,e=t.R_MAJOR,i=t.R_MINOR;return new n.Transformation(.5/(Math.PI*e),.5,-.5/(Math.PI*i),.5)}()}),n.TileLayer=n.Class.extend({includes:n.Mixin.Events,options:{minZoom:0,maxZoom:18,tileSize:256,subdomains:"abc",errorTileUrl:"",attribution:"",zoomOffset:0,opacity:1,unloadInvisibleTiles:n.Browser.mobile,updateWhenIdle:n.Browser.mobile},initialize:function(t,e){e=n.setOptions(this,e),e.detectRetina&&n.Browser.retina&&e.maxZoom>0&&(e.tileSize=Math.floor(e.tileSize/2),e.zoomOffset++,e.minZoom>0&&e.minZoom--,this.options.maxZoom--),this._url=t;var i=this.options.subdomains;"string"==typeof i&&(this.options.subdomains=i.split(""))},onAdd:function(t){this._map=t,this._initContainer(),this._createTileProto(),t.on({viewreset:this._resetCallback,moveend:this._update},this),this.options.updateWhenIdle||(this._limitedUpdate=n.Util.limitExecByInterval(this._update,150,this),t.on("move",this._limitedUpdate,this)),this._reset(),this._update()},addTo:function(t){return t.addLayer(this),this},onRemove:function(t){this._container.parentNode.removeChild(this._container),t.off({viewreset:this._resetCallback,moveend:this._update},this),this.options.updateWhenIdle||t.off("move",this._limitedUpdate,this),this._container=null,this._map=null},bringToFront:function(){var t=this._map._panes.tilePane;return this._container&&(t.appendChild(this._container),this._setAutoZIndex(t,Math.max)),this},bringToBack:function(){var t=this._map._panes.tilePane;return this._container&&(t.insertBefore(this._container,t.firstChild),this._setAutoZIndex(t,Math.min)),this},getAttribution:function(){return this.options.attribution},setOpacity:function(t){return this.options.opacity=t,this._map&&this._updateOpacity(),this},setZIndex:function(t){return this.options.zIndex=t,this._updateZIndex(),this},setUrl:function(t,e){return this._url=t,e||this.redraw(),this},redraw:function(){return this._map&&(this._map._panes.tilePane.empty=!1,this._reset(!0),this._update()),this},_updateZIndex:function(){this._container&&this.options.zIndex!==i&&(this._container.style.zIndex=this.options.zIndex)},_setAutoZIndex:function(t,e){var i,n,o,s=t.children,a=-e(1/0,-1/0);for(n=0,o=s.length;o>n;n++)s[n]!==this._container&&(i=parseInt(s[n].style.zIndex,10),isNaN(i)||(a=e(a,i)));this.options.zIndex=this._container.style.zIndex=(isFinite(a)?a:0)+e(1,-1)},_updateOpacity:function(){n.DomUtil.setOpacity(this._container,this.options.opacity);var t,e=this._tiles;if(n.Browser.webkit)for(t in e)e.hasOwnProperty(t)&&(e[t].style.webkitTransform+=" translate(0,0)")},_initContainer:function(){var t=this._map._panes.tilePane;(!this._container||t.empty)&&(this._container=n.DomUtil.create("div","leaflet-layer"),this._updateZIndex(),t.appendChild(this._container),1>this.options.opacity&&this._updateOpacity())},_resetCallback:function(t){this._reset(t.hard)},_reset:function(t){var e=this._tiles;for(var i in e)e.hasOwnProperty(i)&&this.fire("tileunload",{tile:e[i]});this._tiles={},this._tilesToLoad=0,this.options.reuseTiles&&(this._unusedTiles=[]),t&&this._container&&(this._container.innerHTML=""),this._initContainer()},_update:function(){if(this._map){var t=this._map.getPixelBounds(),e=this._map.getZoom(),i=this.options.tileSize;if(!(e>this.options.maxZoom||this.options.minZoom>e)){var o=new n.Point(Math.floor(t.min.x/i),Math.floor(t.min.y/i)),s=new n.Point(Math.floor(t.max.x/i),Math.floor(t.max.y/i)),a=new n.Bounds(o,s);this._addTilesFromCenterOut(a),(this.options.unloadInvisibleTiles||this.options.reuseTiles)&&this._removeOtherTiles(a)}}},_addTilesFromCenterOut:function(t){var i,o,s,a=[],r=t.getCenter();for(i=t.min.y;t.max.y>=i;i++)for(o=t.min.x;t.max.x>=o;o++)s=new n.Point(o,i),this._tileShouldBeLoaded(s)&&a.push(s);var h=a.length;if(0!==h){a.sort(function(t,e){return t.distanceTo(r)-e.distanceTo(r)});var l=e.createDocumentFragment();for(this._tilesToLoad||this.fire("loading"),this._tilesToLoad+=h,o=0;h>o;o++)this._addTile(a[o],l);this._container.appendChild(l)}},_tileShouldBeLoaded:function(t){if(t.x+":"+t.y in this._tiles)return!1;if(!this.options.continuousWorld){var e=this._getWrapTileNum();if(this.options.noWrap&&(0>t.x||t.x>=e)||0>t.y||t.y>=e)return!1}return!0},_removeOtherTiles:function(t){var e,i,n,o;for(o in this._tiles)this._tiles.hasOwnProperty(o)&&(e=o.split(":"),i=parseInt(e[0],10),n=parseInt(e[1],10),(t.min.x>i||i>t.max.x||t.min.y>n||n>t.max.y)&&this._removeTile(o))},_removeTile:function(t){var e=this._tiles[t];this.fire("tileunload",{tile:e,url:e.src}),this.options.reuseTiles?(n.DomUtil.removeClass(e,"leaflet-tile-loaded"),this._unusedTiles.push(e)):e.parentNode===this._container&&this._container.removeChild(e),n.Browser.android||(e.src=n.Util.emptyImageUrl),delete this._tiles[t]},_addTile:function(t,e){var i=this._getTilePos(t),o=this._getTile();n.DomUtil.setPosition(o,i,n.Browser.chrome||n.Browser.android23),this._tiles[t.x+":"+t.y]=o,this._loadTile(o,t),o.parentNode!==this._container&&e.appendChild(o)
+},_getZoomForUrl:function(){var t=this.options,e=this._map.getZoom();return t.zoomReverse&&(e=t.maxZoom-e),e+t.zoomOffset},_getTilePos:function(t){var e=this._map.getPixelOrigin(),i=this.options.tileSize;return t.multiplyBy(i).subtract(e)},getTileUrl:function(t){return this._adjustTilePoint(t),n.Util.template(this._url,n.extend({s:this._getSubdomain(t),z:this._getZoomForUrl(),x:t.x,y:t.y},this.options))},_getWrapTileNum:function(){return Math.pow(2,this._getZoomForUrl())},_adjustTilePoint:function(t){var e=this._getWrapTileNum();this.options.continuousWorld||this.options.noWrap||(t.x=(t.x%e+e)%e),this.options.tms&&(t.y=e-t.y-1)},_getSubdomain:function(t){var e=(t.x+t.y)%this.options.subdomains.length;return this.options.subdomains[e]},_createTileProto:function(){var t=this._tileImg=n.DomUtil.create("img","leaflet-tile");t.style.width=t.style.height=this.options.tileSize+"px",t.galleryimg="no"},_getTile:function(){if(this.options.reuseTiles&&this._unusedTiles.length>0){var t=this._unusedTiles.pop();return this._resetTile(t),t}return this._createTile()},_resetTile:function(){},_createTile:function(){var t=this._tileImg.cloneNode(!1);return t.onselectstart=t.onmousemove=n.Util.falseFn,t},_loadTile:function(t,e){t._layer=this,t.onload=this._tileOnLoad,t.onerror=this._tileOnError,t.src=this.getTileUrl(e)},_tileLoaded:function(){this._tilesToLoad--,this._tilesToLoad||this.fire("load")},_tileOnLoad:function(){var t=this._layer;this.src!==n.Util.emptyImageUrl&&(n.DomUtil.addClass(this,"leaflet-tile-loaded"),t.fire("tileload",{tile:this,url:this.src})),t._tileLoaded()},_tileOnError:function(){var t=this._layer;t.fire("tileerror",{tile:this,url:this.src});var e=t.options.errorTileUrl;e&&(this.src=e),t._tileLoaded()}}),n.tileLayer=function(t,e){return new n.TileLayer(t,e)},n.TileLayer.WMS=n.TileLayer.extend({defaultWmsParams:{service:"WMS",request:"GetMap",version:"1.1.1",layers:"",styles:"",format:"image/jpeg",transparent:!1},initialize:function(t,e){this._url=t;var i=n.extend({},this.defaultWmsParams);i.width=i.height=e.detectRetina&&n.Browser.retina?2*this.options.tileSize:this.options.tileSize;for(var o in e)this.options.hasOwnProperty(o)||(i[o]=e[o]);this.wmsParams=i,n.setOptions(this,e)},onAdd:function(t){var e=parseFloat(this.wmsParams.version)>=1.3?"crs":"srs";this.wmsParams[e]=t.options.crs.code,n.TileLayer.prototype.onAdd.call(this,t)},getTileUrl:function(t,e){this._adjustTilePoint(t);var i=this._map,o=i.options.crs,s=this.options.tileSize,a=t.multiplyBy(s),r=a.add(new n.Point(s,s)),h=o.project(i.unproject(a,e)),l=o.project(i.unproject(r,e)),u=[h.x,l.y,l.x,h.y].join(","),c=n.Util.template(this._url,{s:this._getSubdomain(t)});return c+n.Util.getParamString(this.wmsParams,c)+"&bbox="+u},setParams:function(t,e){return n.extend(this.wmsParams,t),e||this.redraw(),this}}),n.tileLayer.wms=function(t,e){return new n.TileLayer.WMS(t,e)},n.TileLayer.Canvas=n.TileLayer.extend({options:{async:!1},initialize:function(t){n.setOptions(this,t)},redraw:function(){var t=this._tiles;for(var e in t)t.hasOwnProperty(e)&&this._redrawTile(t[e])},_redrawTile:function(t){this.drawTile(t,t._tilePoint,this._map._zoom)},_createTileProto:function(){var t=this._canvasProto=n.DomUtil.create("canvas","leaflet-tile");t.width=t.height=this.options.tileSize},_createTile:function(){var t=this._canvasProto.cloneNode(!1);return t.onselectstart=t.onmousemove=n.Util.falseFn,t},_loadTile:function(t,e){t._layer=this,t._tilePoint=e,this._redrawTile(t),this.options.async||this.tileDrawn(t)},drawTile:function(){},tileDrawn:function(t){this._tileOnLoad.call(t)}}),n.tileLayer.canvas=function(t){return new n.TileLayer.Canvas(t)},n.ImageOverlay=n.Class.extend({includes:n.Mixin.Events,options:{opacity:1},initialize:function(t,e,i){this._url=t,this._bounds=n.latLngBounds(e),n.setOptions(this,i)},onAdd:function(t){this._map=t,this._image||this._initImage(),t._panes.overlayPane.appendChild(this._image),t.on("viewreset",this._reset,this),t.options.zoomAnimation&&n.Browser.any3d&&t.on("zoomanim",this._animateZoom,this),this._reset()},onRemove:function(t){t.getPanes().overlayPane.removeChild(this._image),t.off("viewreset",this._reset,this),t.options.zoomAnimation&&t.off("zoomanim",this._animateZoom,this)},addTo:function(t){return t.addLayer(this),this},setOpacity:function(t){return this.options.opacity=t,this._updateOpacity(),this},bringToFront:function(){return this._image&&this._map._panes.overlayPane.appendChild(this._image),this},bringToBack:function(){var t=this._map._panes.overlayPane;return this._image&&t.insertBefore(this._image,t.firstChild),this},_initImage:function(){this._image=n.DomUtil.create("img","leaflet-image-layer"),this._map.options.zoomAnimation&&n.Browser.any3d?n.DomUtil.addClass(this._image,"leaflet-zoom-animated"):n.DomUtil.addClass(this._image,"leaflet-zoom-hide"),this._updateOpacity(),n.extend(this._image,{galleryimg:"no",onselectstart:n.Util.falseFn,onmousemove:n.Util.falseFn,onload:n.bind(this._onImageLoad,this),src:this._url})},_animateZoom:function(t){var e=this._map,i=this._image,o=e.getZoomScale(t.zoom),s=this._bounds.getNorthWest(),a=this._bounds.getSouthEast(),r=e._latLngToNewLayerPoint(s,t.zoom,t.center),h=e._latLngToNewLayerPoint(a,t.zoom,t.center)._subtract(r),l=r._add(h._multiplyBy(.5*(1-1/o)));i.style[n.DomUtil.TRANSFORM]=n.DomUtil.getTranslateString(l)+" scale("+o+") "},_reset:function(){var t=this._image,e=this._map.latLngToLayerPoint(this._bounds.getNorthWest()),i=this._map.latLngToLayerPoint(this._bounds.getSouthEast())._subtract(e);n.DomUtil.setPosition(t,e),t.style.width=i.x+"px",t.style.height=i.y+"px"},_onImageLoad:function(){this.fire("load")},_updateOpacity:function(){n.DomUtil.setOpacity(this._image,this.options.opacity)}}),n.imageOverlay=function(t,e,i){return new n.ImageOverlay(t,e,i)},n.Icon=n.Class.extend({options:{className:""},initialize:function(t){n.setOptions(this,t)},createIcon:function(){return this._createIcon("icon")},createShadow:function(){return this._createIcon("shadow")},_createIcon:function(t){var e=this._getIconUrl(t);if(!e){if("icon"===t)throw Error("iconUrl not set in Icon options (see the docs).");return null}var i=this._createImg(e);return this._setIconStyles(i,t),i},_setIconStyles:function(t,e){var i,o=this.options,s=n.point(o[e+"Size"]);i="shadow"===e?n.point(o.shadowAnchor||o.iconAnchor):n.point(o.iconAnchor),!i&&s&&(i=s.divideBy(2,!0)),t.className="leaflet-marker-"+e+" "+o.className,i&&(t.style.marginLeft=-i.x+"px",t.style.marginTop=-i.y+"px"),s&&(t.style.width=s.x+"px",t.style.height=s.y+"px")},_createImg:function(t){var i;return n.Browser.ie6?(i=e.createElement("div"),i.style.filter='progid:DXImageTransform.Microsoft.AlphaImageLoader(src="'+t+'")'):(i=e.createElement("img"),i.src=t),i},_getIconUrl:function(t){return n.Browser.retina&&this.options[t+"RetinaUrl"]?this.options[t+"RetinaUrl"]:this.options[t+"Url"]}}),n.icon=function(t){return new n.Icon(t)},n.Icon.Default=n.Icon.extend({options:{iconSize:new n.Point(25,41),iconAnchor:new n.Point(12,41),popupAnchor:new n.Point(1,-34),shadowSize:new n.Point(41,41)},_getIconUrl:function(t){var e=t+"Url";if(this.options[e])return this.options[e];n.Browser.retina&&"icon"===t&&(t+="@2x");var i=n.Icon.Default.imagePath;if(!i)throw Error("Couldn't autodetect L.Icon.Default.imagePath, set it manually.");return i+"/marker-"+t+".png"}}),n.Icon.Default.imagePath=function(){var t,i,n,o,s=e.getElementsByTagName("script"),a=/\/?leaflet[\-\._]?([\w\-\._]*)\.js\??/;for(t=0,i=s.length;i>t;t++)if(n=s[t].src,o=n.match(a))return n.split(a)[0]+"/images"}(),n.Marker=n.Class.extend({includes:n.Mixin.Events,options:{icon:new n.Icon.Default,title:"",clickable:!0,draggable:!1,zIndexOffset:0,opacity:1,riseOnHover:!1,riseOffset:250},initialize:function(t,e){n.setOptions(this,e),this._latlng=n.latLng(t)},onAdd:function(t){this._map=t,t.on("viewreset",this.update,this),this._initIcon(),this.update(),t.options.zoomAnimation&&t.options.markerZoomAnimation&&t.on("zoomanim",this._animateZoom,this)},addTo:function(t){return t.addLayer(this),this},onRemove:function(t){this._removeIcon(),this.fire("remove"),t.off({viewreset:this.update,zoomanim:this._animateZoom},this),this._map=null},getLatLng:function(){return this._latlng},setLatLng:function(t){return this._latlng=n.latLng(t),this.update(),this.fire("move",{latlng:this._latlng})},setZIndexOffset:function(t){return this.options.zIndexOffset=t,this.update(),this},setIcon:function(t){return this._map&&this._removeIcon(),this.options.icon=t,this._map&&(this._initIcon(),this.update()),this},update:function(){if(this._icon){var t=this._map.latLngToLayerPoint(this._latlng).round();this._setPos(t)}return this},_initIcon:function(){var t=this.options,e=this._map,i=e.options.zoomAnimation&&e.options.markerZoomAnimation,o=i?"leaflet-zoom-animated":"leaflet-zoom-hide",s=!1;this._icon||(this._icon=t.icon.createIcon(),t.title&&(this._icon.title=t.title),this._initInteraction(),s=1>this.options.opacity,n.DomUtil.addClass(this._icon,o),t.riseOnHover&&n.DomEvent.on(this._icon,"mouseover",this._bringToFront,this).on(this._icon,"mouseout",this._resetZIndex,this)),this._shadow||(this._shadow=t.icon.createShadow(),this._shadow&&(n.DomUtil.addClass(this._shadow,o),s=1>this.options.opacity)),s&&this._updateOpacity();var a=this._map._panes;a.markerPane.appendChild(this._icon),this._shadow&&a.shadowPane.appendChild(this._shadow)},_removeIcon:function(){var t=this._map._panes;this.options.riseOnHover&&n.DomEvent.off(this._icon,"mouseover",this._bringToFront).off(this._icon,"mouseout",this._resetZIndex),t.markerPane.removeChild(this._icon),this._shadow&&t.shadowPane.removeChild(this._shadow),this._icon=this._shadow=null},_setPos:function(t){n.DomUtil.setPosition(this._icon,t),this._shadow&&n.DomUtil.setPosition(this._shadow,t),this._zIndex=t.y+this.options.zIndexOffset,this._resetZIndex()},_updateZIndex:function(t){this._icon.style.zIndex=this._zIndex+t},_animateZoom:function(t){var e=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center);this._setPos(e)},_initInteraction:function(){if(this.options.clickable){var t=this._icon,e=["dblclick","mousedown","mouseover","mouseout","contextmenu"];n.DomUtil.addClass(t,"leaflet-clickable"),n.DomEvent.on(t,"click",this._onMouseClick,this);for(var i=0;e.length>i;i++)n.DomEvent.on(t,e[i],this._fireMouseEvent,this);n.Handler.MarkerDrag&&(this.dragging=new n.Handler.MarkerDrag(this),this.options.draggable&&this.dragging.enable())}},_onMouseClick:function(t){var e=this.dragging&&this.dragging.moved();(this.hasEventListeners(t.type)||e)&&n.DomEvent.stopPropagation(t),e||(this.dragging&&this.dragging._enabled||!this._map.dragging||!this._map.dragging.moved())&&this.fire(t.type,{originalEvent:t})},_fireMouseEvent:function(t){this.fire(t.type,{originalEvent:t}),"contextmenu"===t.type&&this.hasEventListeners(t.type)&&n.DomEvent.preventDefault(t),"mousedown"!==t.type&&n.DomEvent.stopPropagation(t)},setOpacity:function(t){this.options.opacity=t,this._map&&this._updateOpacity()},_updateOpacity:function(){n.DomUtil.setOpacity(this._icon,this.options.opacity),this._shadow&&n.DomUtil.setOpacity(this._shadow,this.options.opacity)},_bringToFront:function(){this._updateZIndex(this.options.riseOffset)},_resetZIndex:function(){this._updateZIndex(0)}}),n.marker=function(t,e){return new n.Marker(t,e)},n.DivIcon=n.Icon.extend({options:{iconSize:new n.Point(12,12),className:"leaflet-div-icon"},createIcon:function(){var t=e.createElement("div"),i=this.options;return i.html&&(t.innerHTML=i.html),i.bgPos&&(t.style.backgroundPosition=-i.bgPos.x+"px "+-i.bgPos.y+"px"),this._setIconStyles(t,"icon"),t},createShadow:function(){return null}}),n.divIcon=function(t){return new n.DivIcon(t)},n.Map.mergeOptions({closePopupOnClick:!0}),n.Popup=n.Class.extend({includes:n.Mixin.Events,options:{minWidth:50,maxWidth:300,maxHeight:null,autoPan:!0,closeButton:!0,offset:new n.Point(0,6),autoPanPadding:new n.Point(5,5),className:"",zoomAnimation:!0},initialize:function(t,e){n.setOptions(this,t),this._source=e,this._animated=n.Browser.any3d&&this.options.zoomAnimation},onAdd:function(t){this._map=t,this._container||this._initLayout(),this._updateContent();var e=t.options.fadeAnimation;e&&n.DomUtil.setOpacity(this._container,0),t._panes.popupPane.appendChild(this._container),t.on("viewreset",this._updatePosition,this),this._animated&&t.on("zoomanim",this._zoomAnimation,this),t.options.closePopupOnClick&&t.on("preclick",this._close,this),this._update(),e&&n.DomUtil.setOpacity(this._container,1)},addTo:function(t){return t.addLayer(this),this},openOn:function(t){return t.openPopup(this),this},onRemove:function(t){t._panes.popupPane.removeChild(this._container),n.Util.falseFn(this._container.offsetWidth),t.off({viewreset:this._updatePosition,preclick:this._close,zoomanim:this._zoomAnimation},this),t.options.fadeAnimation&&n.DomUtil.setOpacity(this._container,0),this._map=null},setLatLng:function(t){return this._latlng=n.latLng(t),this._update(),this},setContent:function(t){return this._content=t,this._update(),this},_close:function(){var t=this._map;t&&(t._popup=null,t.removeLayer(this).fire("popupclose",{popup:this}))},_initLayout:function(){var t,e="leaflet-popup",i=e+" "+this.options.className+" leaflet-zoom-"+(this._animated?"animated":"hide"),o=this._container=n.DomUtil.create("div",i);this.options.closeButton&&(t=this._closeButton=n.DomUtil.create("a",e+"-close-button",o),t.href="#close",t.innerHTML="×",n.DomEvent.on(t,"click",this._onCloseButtonClick,this));var s=this._wrapper=n.DomUtil.create("div",e+"-content-wrapper",o);n.DomEvent.disableClickPropagation(s),this._contentNode=n.DomUtil.create("div",e+"-content",s),n.DomEvent.on(this._contentNode,"mousewheel",n.DomEvent.stopPropagation),this._tipContainer=n.DomUtil.create("div",e+"-tip-container",o),this._tip=n.DomUtil.create("div",e+"-tip",this._tipContainer)},_update:function(){this._map&&(this._container.style.visibility="hidden",this._updateContent(),this._updateLayout(),this._updatePosition(),this._container.style.visibility="",this._adjustPan())},_updateContent:function(){if(this._content){if("string"==typeof this._content)this._contentNode.innerHTML=this._content;else{for(;this._contentNode.hasChildNodes();)this._contentNode.removeChild(this._contentNode.firstChild);this._contentNode.appendChild(this._content)}this.fire("contentupdate")}},_updateLayout:function(){var t=this._contentNode,e=t.style;e.width="",e.whiteSpace="nowrap";var i=t.offsetWidth;i=Math.min(i,this.options.maxWidth),i=Math.max(i,this.options.minWidth),e.width=i+1+"px",e.whiteSpace="",e.height="";var o=t.offsetHeight,s=this.options.maxHeight,a="leaflet-popup-scrolled";s&&o>s?(e.height=s+"px",n.DomUtil.addClass(t,a)):n.DomUtil.removeClass(t,a),this._containerWidth=this._container.offsetWidth},_updatePosition:function(){if(this._map){var t=this._map.latLngToLayerPoint(this._latlng),e=this._animated,i=this.options.offset;e&&n.DomUtil.setPosition(this._container,t),this._containerBottom=-i.y-(e?0:t.y),this._containerLeft=-Math.round(this._containerWidth/2)+i.x+(e?0:t.x),this._container.style.bottom=this._containerBottom+"px",this._container.style.left=this._containerLeft+"px"}},_zoomAnimation:function(t){var e=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center);n.DomUtil.setPosition(this._container,e)},_adjustPan:function(){if(this.options.autoPan){var t=this._map,e=this._container.offsetHeight,i=this._containerWidth,o=new n.Point(this._containerLeft,-e-this._containerBottom);this._animated&&o._add(n.DomUtil.getPosition(this._container));var s=t.layerPointToContainerPoint(o),a=this.options.autoPanPadding,r=t.getSize(),h=0,l=0;0>s.x&&(h=s.x-a.x),s.x+i>r.x&&(h=s.x+i-r.x+a.x),0>s.y&&(l=s.y-a.y),s.y+e>r.y&&(l=s.y+e-r.y+a.y),(h||l)&&t.panBy(new n.Point(h,l))}},_onCloseButtonClick:function(t){this._close(),n.DomEvent.stop(t)}}),n.popup=function(t,e){return new n.Popup(t,e)},n.Marker.include({openPopup:function(){return this._popup&&this._map&&(this._popup.setLatLng(this._latlng),this._map.openPopup(this._popup)),this},closePopup:function(){return this._popup&&this._popup._close(),this},bindPopup:function(t,e){var i=n.point(this.options.icon.options.popupAnchor)||new n.Point(0,0);return i=i.add(n.Popup.prototype.options.offset),e&&e.offset&&(i=i.add(e.offset)),e=n.extend({offset:i},e),this._popup||this.on("click",this.openPopup,this).on("remove",this.closePopup,this).on("move",this._movePopup,this),this._popup=new n.Popup(e,this).setContent(t),this},unbindPopup:function(){return this._popup&&(this._popup=null,this.off("click",this.openPopup).off("remove",this.closePopup).off("move",this._movePopup)),this},_movePopup:function(t){this._popup.setLatLng(t.latlng)}}),n.Map.include({openPopup:function(t){return this.closePopup(),this._popup=t,this.addLayer(t).fire("popupopen",{popup:this._popup})},closePopup:function(){return this._popup&&this._popup._close(),this}}),n.LayerGroup=n.Class.extend({initialize:function(t){this._layers={};var e,i;if(t)for(e=0,i=t.length;i>e;e++)this.addLayer(t[e])},addLayer:function(t){var e=n.stamp(t);return this._layers[e]=t,this._map&&this._map.addLayer(t),this},removeLayer:function(t){var e=n.stamp(t);return delete this._layers[e],this._map&&this._map.removeLayer(t),this},clearLayers:function(){return this.eachLayer(this.removeLayer,this),this},invoke:function(t){var e,i,n=Array.prototype.slice.call(arguments,1);for(e in this._layers)this._layers.hasOwnProperty(e)&&(i=this._layers[e],i[t]&&i[t].apply(i,n));return this},onAdd:function(t){this._map=t,this.eachLayer(t.addLayer,t)},onRemove:function(t){this.eachLayer(t.removeLayer,t),this._map=null},addTo:function(t){return t.addLayer(this),this},eachLayer:function(t,e){for(var i in this._layers)this._layers.hasOwnProperty(i)&&t.call(e,this._layers[i])},setZIndex:function(t){return this.invoke("setZIndex",t)}}),n.layerGroup=function(t){return new n.LayerGroup(t)},n.FeatureGroup=n.LayerGroup.extend({includes:n.Mixin.Events,statics:{EVENTS:"click dblclick mouseover mouseout mousemove contextmenu"},addLayer:function(t){return this._layers[n.stamp(t)]?this:(t.on(n.FeatureGroup.EVENTS,this._propagateEvent,this),n.LayerGroup.prototype.addLayer.call(this,t),this._popupContent&&t.bindPopup&&t.bindPopup(this._popupContent,this._popupOptions),this.fire("layeradd",{layer:t}))},removeLayer:function(t){return t.off(n.FeatureGroup.EVENTS,this._propagateEvent,this),n.LayerGroup.prototype.removeLayer.call(this,t),this._popupContent&&this.invoke("unbindPopup"),this.fire("layerremove",{layer:t})},bindPopup:function(t,e){return this._popupContent=t,this._popupOptions=e,this.invoke("bindPopup",t,e)},setStyle:function(t){return this.invoke("setStyle",t)},bringToFront:function(){return this.invoke("bringToFront")},bringToBack:function(){return this.invoke("bringToBack")},getBounds:function(){var t=new n.LatLngBounds;return this.eachLayer(function(e){t.extend(e instanceof n.Marker?e.getLatLng():e.getBounds())}),t},_propagateEvent:function(t){t.layer=t.target,t.target=this,this.fire(t.type,t)}}),n.featureGroup=function(t){return new n.FeatureGroup(t)},n.Path=n.Class.extend({includes:[n.Mixin.Events],statics:{CLIP_PADDING:n.Browser.mobile?Math.max(0,Math.min(.5,(1280/Math.max(t.innerWidth,t.innerHeight)-1)/2)):.5},options:{stroke:!0,color:"#0033ff",dashArray:null,weight:5,opacity:.5,fill:!1,fillColor:null,fillOpacity:.2,clickable:!0},initialize:function(t){n.setOptions(this,t)},onAdd:function(t){this._map=t,this._container||(this._initElements(),this._initEvents()),this.projectLatlngs(),this._updatePath(),this._container&&this._map._pathRoot.appendChild(this._container),this.fire("add"),t.on({viewreset:this.projectLatlngs,moveend:this._updatePath},this)},addTo:function(t){return t.addLayer(this),this},onRemove:function(t){t._pathRoot.removeChild(this._container),this.fire("remove"),this._map=null,n.Browser.vml&&(this._container=null,this._stroke=null,this._fill=null),t.off({viewreset:this.projectLatlngs,moveend:this._updatePath},this)},projectLatlngs:function(){},setStyle:function(t){return n.setOptions(this,t),this._container&&this._updateStyle(),this},redraw:function(){return this._map&&(this.projectLatlngs(),this._updatePath()),this}}),n.Map.include({_updatePathViewport:function(){var t=n.Path.CLIP_PADDING,e=this.getSize(),i=n.DomUtil.getPosition(this._mapPane),o=i.multiplyBy(-1)._subtract(e.multiplyBy(t)._round()),s=o.add(e.multiplyBy(1+2*t)._round());this._pathViewport=new n.Bounds(o,s)}}),n.Path.SVG_NS="http://www.w3.org/2000/svg",n.Browser.svg=!(!e.createElementNS||!e.createElementNS(n.Path.SVG_NS,"svg").createSVGRect),n.Path=n.Path.extend({statics:{SVG:n.Browser.svg},bringToFront:function(){var t=this._map._pathRoot,e=this._container;return e&&t.lastChild!==e&&t.appendChild(e),this},bringToBack:function(){var t=this._map._pathRoot,e=this._container,i=t.firstChild;return e&&i!==e&&t.insertBefore(e,i),this},getPathString:function(){},_createElement:function(t){return e.createElementNS(n.Path.SVG_NS,t)},_initElements:function(){this._map._initPathRoot(),this._initPath(),this._initStyle()},_initPath:function(){this._container=this._createElement("g"),this._path=this._createElement("path"),this._container.appendChild(this._path)},_initStyle:function(){this.options.stroke&&(this._path.setAttribute("stroke-linejoin","round"),this._path.setAttribute("stroke-linecap","round")),this.options.fill&&this._path.setAttribute("fill-rule","evenodd"),this._updateStyle()},_updateStyle:function(){this.options.stroke?(this._path.setAttribute("stroke",this.options.color),this._path.setAttribute("stroke-opacity",this.options.opacity),this._path.setAttribute("stroke-width",this.options.weight),this.options.dashArray?this._path.setAttribute("stroke-dasharray",this.options.dashArray):this._path.removeAttribute("stroke-dasharray")):this._path.setAttribute("stroke","none"),this.options.fill?(this._path.setAttribute("fill",this.options.fillColor||this.options.color),this._path.setAttribute("fill-opacity",this.options.fillOpacity)):this._path.setAttribute("fill","none")},_updatePath:function(){var t=this.getPathString();t||(t="M0 0"),this._path.setAttribute("d",t)},_initEvents:function(){if(this.options.clickable){(n.Browser.svg||!n.Browser.vml)&&this._path.setAttribute("class","leaflet-clickable"),n.DomEvent.on(this._container,"click",this._onMouseClick,this);for(var t=["dblclick","mousedown","mouseover","mouseout","mousemove","contextmenu"],e=0;t.length>e;e++)n.DomEvent.on(this._container,t[e],this._fireMouseEvent,this)}},_onMouseClick:function(t){this._map.dragging&&this._map.dragging.moved()||this._fireMouseEvent(t)},_fireMouseEvent:function(t){if(this.hasEventListeners(t.type)){var e=this._map,i=e.mouseEventToContainerPoint(t),o=e.containerPointToLayerPoint(i),s=e.layerPointToLatLng(o);this.fire(t.type,{latlng:s,layerPoint:o,containerPoint:i,originalEvent:t}),"contextmenu"===t.type&&n.DomEvent.preventDefault(t),"mousemove"!==t.type&&n.DomEvent.stopPropagation(t)}}}),n.Map.include({_initPathRoot:function(){this._pathRoot||(this._pathRoot=n.Path.prototype._createElement("svg"),this._panes.overlayPane.appendChild(this._pathRoot),this.options.zoomAnimation&&n.Browser.any3d?(this._pathRoot.setAttribute("class"," leaflet-zoom-animated"),this.on({zoomanim:this._animatePathZoom,zoomend:this._endPathZoom})):this._pathRoot.setAttribute("class"," leaflet-zoom-hide"),this.on("moveend",this._updateSvgViewport),this._updateSvgViewport())},_animatePathZoom:function(t){var e=this.getZoomScale(t.zoom),i=this._getCenterOffset(t.center)._multiplyBy(-e)._add(this._pathViewport.min);this._pathRoot.style[n.DomUtil.TRANSFORM]=n.DomUtil.getTranslateString(i)+" scale("+e+") ",this._pathZooming=!0},_endPathZoom:function(){this._pathZooming=!1},_updateSvgViewport:function(){if(!this._pathZooming){this._updatePathViewport();var t=this._pathViewport,e=t.min,i=t.max,o=i.x-e.x,s=i.y-e.y,a=this._pathRoot,r=this._panes.overlayPane;n.Browser.mobileWebkit&&r.removeChild(a),n.DomUtil.setPosition(a,e),a.setAttribute("width",o),a.setAttribute("height",s),a.setAttribute("viewBox",[e.x,e.y,o,s].join(" ")),n.Browser.mobileWebkit&&r.appendChild(a)}}}),n.Path.include({bindPopup:function(t,e){return(!this._popup||e)&&(this._popup=new n.Popup(e,this)),this._popup.setContent(t),this._popupHandlersAdded||(this.on("click",this._openPopup,this).on("remove",this.closePopup,this),this._popupHandlersAdded=!0),this},unbindPopup:function(){return this._popup&&(this._popup=null,this.off("click",this._openPopup).off("remove",this.closePopup),this._popupHandlersAdded=!1),this},openPopup:function(t){return this._popup&&(t=t||this._latlng||this._latlngs[Math.floor(this._latlngs.length/2)],this._openPopup({latlng:t})),this},closePopup:function(){return this._popup&&this._popup._close(),this},_openPopup:function(t){this._popup.setLatLng(t.latlng),this._map.openPopup(this._popup)}}),n.Browser.vml=!n.Browser.svg&&function(){try{var t=e.createElement("div");t.innerHTML=' ';var i=t.firstChild;return i.style.behavior="url(#default#VML)",i&&"object"==typeof i.adj}catch(n){return!1}}(),n.Path=n.Browser.svg||!n.Browser.vml?n.Path:n.Path.extend({statics:{VML:!0,CLIP_PADDING:.02},_createElement:function(){try{return e.namespaces.add("lvml","urn:schemas-microsoft-com:vml"),function(t){return e.createElement("')}}catch(t){return function(t){return e.createElement("<"+t+' xmlns="urn:schemas-microsoft.com:vml" class="lvml">')}}}(),_initPath:function(){var t=this._container=this._createElement("shape");n.DomUtil.addClass(t,"leaflet-vml-shape"),this.options.clickable&&n.DomUtil.addClass(t,"leaflet-clickable"),t.coordsize="1 1",this._path=this._createElement("path"),t.appendChild(this._path),this._map._pathRoot.appendChild(t)},_initStyle:function(){this._updateStyle()},_updateStyle:function(){var t=this._stroke,e=this._fill,i=this.options,n=this._container;n.stroked=i.stroke,n.filled=i.fill,i.stroke?(t||(t=this._stroke=this._createElement("stroke"),t.endcap="round",n.appendChild(t)),t.weight=i.weight+"px",t.color=i.color,t.opacity=i.opacity,t.dashStyle=i.dashArray?i.dashArray instanceof Array?i.dashArray.join(" "):i.dashArray.replace(/ *, */g," "):""):t&&(n.removeChild(t),this._stroke=null),i.fill?(e||(e=this._fill=this._createElement("fill"),n.appendChild(e)),e.color=i.fillColor||i.color,e.opacity=i.fillOpacity):e&&(n.removeChild(e),this._fill=null)},_updatePath:function(){var t=this._container.style;t.display="none",this._path.v=this.getPathString()+" ",t.display=""}}),n.Map.include(n.Browser.svg||!n.Browser.vml?{}:{_initPathRoot:function(){if(!this._pathRoot){var t=this._pathRoot=e.createElement("div");t.className="leaflet-vml-container",this._panes.overlayPane.appendChild(t),this.on("moveend",this._updatePathViewport),this._updatePathViewport()}}}),n.Browser.canvas=function(){return!!e.createElement("canvas").getContext}(),n.Path=n.Path.SVG&&!t.L_PREFER_CANVAS||!n.Browser.canvas?n.Path:n.Path.extend({statics:{CANVAS:!0,SVG:!1},redraw:function(){return this._map&&(this.projectLatlngs(),this._requestUpdate()),this},setStyle:function(t){return n.setOptions(this,t),this._map&&(this._updateStyle(),this._requestUpdate()),this},onRemove:function(t){t.off("viewreset",this.projectLatlngs,this).off("moveend",this._updatePath,this),this.options.clickable&&this._map.off("click",this._onClick,this),this._requestUpdate(),this._map=null},_requestUpdate:function(){this._map&&!n.Path._updateRequest&&(n.Path._updateRequest=n.Util.requestAnimFrame(this._fireMapMoveEnd,this._map))},_fireMapMoveEnd:function(){n.Path._updateRequest=null,this.fire("moveend")},_initElements:function(){this._map._initPathRoot(),this._ctx=this._map._canvasCtx},_updateStyle:function(){var t=this.options;t.stroke&&(this._ctx.lineWidth=t.weight,this._ctx.strokeStyle=t.color),t.fill&&(this._ctx.fillStyle=t.fillColor||t.color)},_drawPath:function(){var t,e,i,o,s,a;for(this._ctx.beginPath(),t=0,i=this._parts.length;i>t;t++){for(e=0,o=this._parts[t].length;o>e;e++)s=this._parts[t][e],a=(0===e?"move":"line")+"To",this._ctx[a](s.x,s.y);this instanceof n.Polygon&&this._ctx.closePath()}},_checkIfEmpty:function(){return!this._parts.length},_updatePath:function(){if(!this._checkIfEmpty()){var t=this._ctx,e=this.options;this._drawPath(),t.save(),this._updateStyle(),e.fill&&(t.globalAlpha=e.fillOpacity,t.fill()),e.stroke&&(t.globalAlpha=e.opacity,t.stroke()),t.restore()}},_initEvents:function(){this.options.clickable&&this._map.on("click",this._onClick,this)},_onClick:function(t){this._containsPoint(t.layerPoint)&&this.fire("click",{latlng:t.latlng,layerPoint:t.layerPoint,containerPoint:t.containerPoint,originalEvent:t})}}),n.Map.include(n.Path.SVG&&!t.L_PREFER_CANVAS||!n.Browser.canvas?{}:{_initPathRoot:function(){var t,i=this._pathRoot;i||(i=this._pathRoot=e.createElement("canvas"),i.style.position="absolute",t=this._canvasCtx=i.getContext("2d"),t.lineCap="round",t.lineJoin="round",this._panes.overlayPane.appendChild(i),this.options.zoomAnimation&&(this._pathRoot.className="leaflet-zoom-animated",this.on("zoomanim",this._animatePathZoom),this.on("zoomend",this._endPathZoom)),this.on("moveend",this._updateCanvasViewport),this._updateCanvasViewport())},_updateCanvasViewport:function(){if(!this._pathZooming){this._updatePathViewport();var t=this._pathViewport,e=t.min,i=t.max.subtract(e),o=this._pathRoot;n.DomUtil.setPosition(o,e),o.width=i.x,o.height=i.y,o.getContext("2d").translate(-e.x,-e.y)}}}),n.LineUtil={simplify:function(t,e){if(!e||!t.length)return t.slice();var i=e*e;return t=this._reducePoints(t,i),t=this._simplifyDP(t,i)},pointToSegmentDistance:function(t,e,i){return Math.sqrt(this._sqClosestPointOnSegment(t,e,i,!0))},closestPointOnSegment:function(t,e,i){return this._sqClosestPointOnSegment(t,e,i)},_simplifyDP:function(t,e){var n=t.length,o=typeof Uint8Array!=i+""?Uint8Array:Array,s=new o(n);s[0]=s[n-1]=1,this._simplifyDPStep(t,s,e,0,n-1);var a,r=[];for(a=0;n>a;a++)s[a]&&r.push(t[a]);return r},_simplifyDPStep:function(t,e,i,n,o){var s,a,r,h=0;for(a=n+1;o-1>=a;a++)r=this._sqClosestPointOnSegment(t[a],t[n],t[o],!0),r>h&&(s=a,h=r);h>i&&(e[s]=1,this._simplifyDPStep(t,e,i,n,s),this._simplifyDPStep(t,e,i,s,o))},_reducePoints:function(t,e){for(var i=[t[0]],n=1,o=0,s=t.length;s>n;n++)this._sqDist(t[n],t[o])>e&&(i.push(t[n]),o=n);return s-1>o&&i.push(t[s-1]),i},clipSegment:function(t,e,i,n){var o,s,a,r=n?this._lastCode:this._getBitCode(t,i),h=this._getBitCode(e,i);for(this._lastCode=h;;){if(!(r|h))return[t,e];if(r&h)return!1;o=r||h,s=this._getEdgeIntersection(t,e,o,i),a=this._getBitCode(s,i),o===r?(t=s,r=a):(e=s,h=a)}},_getEdgeIntersection:function(t,e,o,s){var a=e.x-t.x,r=e.y-t.y,h=s.min,l=s.max;return 8&o?new n.Point(t.x+a*(l.y-t.y)/r,l.y):4&o?new n.Point(t.x+a*(h.y-t.y)/r,h.y):2&o?new n.Point(l.x,t.y+r*(l.x-t.x)/a):1&o?new n.Point(h.x,t.y+r*(h.x-t.x)/a):i},_getBitCode:function(t,e){var i=0;return t.xe.max.x&&(i|=2),t.ye.max.y&&(i|=8),i},_sqDist:function(t,e){var i=e.x-t.x,n=e.y-t.y;return i*i+n*n},_sqClosestPointOnSegment:function(t,e,i,o){var s,a=e.x,r=e.y,h=i.x-a,l=i.y-r,u=h*h+l*l;return u>0&&(s=((t.x-a)*h+(t.y-r)*l)/u,s>1?(a=i.x,r=i.y):s>0&&(a+=h*s,r+=l*s)),h=t.x-a,l=t.y-r,o?h*h+l*l:new n.Point(a,r)}},n.Polyline=n.Path.extend({initialize:function(t,e){n.Path.prototype.initialize.call(this,e),this._latlngs=this._convertLatLngs(t)},options:{smoothFactor:1,noClip:!1},projectLatlngs:function(){this._originalPoints=[];for(var t=0,e=this._latlngs.length;e>t;t++)this._originalPoints[t]=this._map.latLngToLayerPoint(this._latlngs[t])},getPathString:function(){for(var t=0,e=this._parts.length,i="";e>t;t++)i+=this._getPathPartStr(this._parts[t]);return i},getLatLngs:function(){return this._latlngs},setLatLngs:function(t){return this._latlngs=this._convertLatLngs(t),this.redraw()},addLatLng:function(t){return this._latlngs.push(n.latLng(t)),this.redraw()},spliceLatLngs:function(){var t=[].splice.apply(this._latlngs,arguments);return this._convertLatLngs(this._latlngs),this.redraw(),t},closestLayerPoint:function(t){for(var e,i,o=1/0,s=this._parts,a=null,r=0,h=s.length;h>r;r++)for(var l=s[r],u=1,c=l.length;c>u;u++){e=l[u-1],i=l[u];
+var _=n.LineUtil._sqClosestPointOnSegment(t,e,i,!0);o>_&&(o=_,a=n.LineUtil._sqClosestPointOnSegment(t,e,i))}return a&&(a.distance=Math.sqrt(o)),a},getBounds:function(){var t,e,i=new n.LatLngBounds,o=this.getLatLngs();for(t=0,e=o.length;e>t;t++)i.extend(o[t]);return i},_convertLatLngs:function(t){var e,i;for(e=0,i=t.length;i>e;e++){if(n.Util.isArray(t[e])&&"number"!=typeof t[e][0])return;t[e]=n.latLng(t[e])}return t},_initEvents:function(){n.Path.prototype._initEvents.call(this)},_getPathPartStr:function(t){for(var e,i=n.Path.VML,o=0,s=t.length,a="";s>o;o++)e=t[o],i&&e._round(),a+=(o?"L":"M")+e.x+" "+e.y;return a},_clipPoints:function(){var t,e,o,s=this._originalPoints,a=s.length;if(this.options.noClip)return this._parts=[s],i;this._parts=[];var r=this._parts,h=this._map._pathViewport,l=n.LineUtil;for(t=0,e=0;a-1>t;t++)o=l.clipSegment(s[t],s[t+1],h,t),o&&(r[e]=r[e]||[],r[e].push(o[0]),(o[1]!==s[t+1]||t===a-2)&&(r[e].push(o[1]),e++))},_simplifyPoints:function(){for(var t=this._parts,e=n.LineUtil,i=0,o=t.length;o>i;i++)t[i]=e.simplify(t[i],this.options.smoothFactor)},_updatePath:function(){this._map&&(this._clipPoints(),this._simplifyPoints(),n.Path.prototype._updatePath.call(this))}}),n.polyline=function(t,e){return new n.Polyline(t,e)},n.PolyUtil={},n.PolyUtil.clipPolygon=function(t,e){var i,o,s,a,r,h,l,u,c,_=[1,4,2,8],d=n.LineUtil;for(o=0,l=t.length;l>o;o++)t[o]._code=d._getBitCode(t[o],e);for(a=0;4>a;a++){for(u=_[a],i=[],o=0,l=t.length,s=l-1;l>o;s=o++)r=t[o],h=t[s],r._code&u?h._code&u||(c=d._getEdgeIntersection(h,r,u,e),c._code=d._getBitCode(c,e),i.push(c)):(h._code&u&&(c=d._getEdgeIntersection(h,r,u,e),c._code=d._getBitCode(c,e),i.push(c)),i.push(r));t=i}return t},n.Polygon=n.Polyline.extend({options:{fill:!0},initialize:function(t,e){n.Polyline.prototype.initialize.call(this,t,e),t&&n.Util.isArray(t[0])&&"number"!=typeof t[0][0]&&(this._latlngs=this._convertLatLngs(t[0]),this._holes=t.slice(1))},projectLatlngs:function(){if(n.Polyline.prototype.projectLatlngs.call(this),this._holePoints=[],this._holes){var t,e,i,o;for(t=0,i=this._holes.length;i>t;t++)for(this._holePoints[t]=[],e=0,o=this._holes[t].length;o>e;e++)this._holePoints[t][e]=this._map.latLngToLayerPoint(this._holes[t][e])}},_clipPoints:function(){var t=this._originalPoints,e=[];if(this._parts=[t].concat(this._holePoints),!this.options.noClip){for(var i=0,o=this._parts.length;o>i;i++){var s=n.PolyUtil.clipPolygon(this._parts[i],this._map._pathViewport);s.length&&e.push(s)}this._parts=e}},_getPathPartStr:function(t){var e=n.Polyline.prototype._getPathPartStr.call(this,t);return e+(n.Browser.svg?"z":"x")}}),n.polygon=function(t,e){return new n.Polygon(t,e)},function(){function t(t){return n.FeatureGroup.extend({initialize:function(t,e){this._layers={},this._options=e,this.setLatLngs(t)},setLatLngs:function(e){var i=0,n=e.length;for(this.eachLayer(function(t){n>i?t.setLatLngs(e[i++]):this.removeLayer(t)},this);n>i;)this.addLayer(new t(e[i++],this._options));return this}})}n.MultiPolyline=t(n.Polyline),n.MultiPolygon=t(n.Polygon),n.multiPolyline=function(t,e){return new n.MultiPolyline(t,e)},n.multiPolygon=function(t,e){return new n.MultiPolygon(t,e)}}(),n.Rectangle=n.Polygon.extend({initialize:function(t,e){n.Polygon.prototype.initialize.call(this,this._boundsToLatLngs(t),e)},setBounds:function(t){this.setLatLngs(this._boundsToLatLngs(t))},_boundsToLatLngs:function(t){return t=n.latLngBounds(t),[t.getSouthWest(),t.getNorthWest(),t.getNorthEast(),t.getSouthEast()]}}),n.rectangle=function(t,e){return new n.Rectangle(t,e)},n.Circle=n.Path.extend({initialize:function(t,e,i){n.Path.prototype.initialize.call(this,i),this._latlng=n.latLng(t),this._mRadius=e},options:{fill:!0},setLatLng:function(t){return this._latlng=n.latLng(t),this.redraw()},setRadius:function(t){return this._mRadius=t,this.redraw()},projectLatlngs:function(){var t=this._getLngRadius(),e=new n.LatLng(this._latlng.lat,this._latlng.lng-t),i=this._map.latLngToLayerPoint(e);this._point=this._map.latLngToLayerPoint(this._latlng),this._radius=Math.max(Math.round(this._point.x-i.x),1)},getBounds:function(){var t=this._getLngRadius(),e=360*(this._mRadius/40075017),i=this._latlng,o=new n.LatLng(i.lat-e,i.lng-t),s=new n.LatLng(i.lat+e,i.lng+t);return new n.LatLngBounds(o,s)},getLatLng:function(){return this._latlng},getPathString:function(){var t=this._point,e=this._radius;return this._checkIfEmpty()?"":n.Browser.svg?"M"+t.x+","+(t.y-e)+"A"+e+","+e+",0,1,1,"+(t.x-.1)+","+(t.y-e)+" z":(t._round(),e=Math.round(e),"AL "+t.x+","+t.y+" "+e+","+e+" 0,"+23592600)},getRadius:function(){return this._mRadius},_getLatRadius:function(){return 360*(this._mRadius/40075017)},_getLngRadius:function(){return this._getLatRadius()/Math.cos(n.LatLng.DEG_TO_RAD*this._latlng.lat)},_checkIfEmpty:function(){if(!this._map)return!1;var t=this._map._pathViewport,e=this._radius,i=this._point;return i.x-e>t.max.x||i.y-e>t.max.y||i.x+ei;i++)for(l=this._parts[i],o=0,r=l.length,s=r-1;r>o;s=o++)if((e||0!==o)&&(h=n.LineUtil.pointToSegmentDistance(t,l[s],l[o]),u>=h))return!0;return!1}}:{}),n.Polygon.include(n.Path.CANVAS?{_containsPoint:function(t){var e,i,o,s,a,r,h,l,u=!1;if(n.Polyline.prototype._containsPoint.call(this,t,!0))return!0;for(s=0,h=this._parts.length;h>s;s++)for(e=this._parts[s],a=0,l=e.length,r=l-1;l>a;r=a++)i=e[a],o=e[r],i.y>t.y!=o.y>t.y&&t.x<(o.x-i.x)*(t.y-i.y)/(o.y-i.y)+i.x&&(u=!u);return u}}:{}),n.Circle.include(n.Path.CANVAS?{_drawPath:function(){var t=this._point;this._ctx.beginPath(),this._ctx.arc(t.x,t.y,this._radius,0,2*Math.PI,!1)},_containsPoint:function(t){var e=this._point,i=this.options.stroke?this.options.weight/2:0;return t.distanceTo(e)<=this._radius+i}}:{}),n.GeoJSON=n.FeatureGroup.extend({initialize:function(t,e){n.setOptions(this,e),this._layers={},t&&this.addData(t)},addData:function(t){var e,i,o=n.Util.isArray(t)?t:t.features;if(o){for(e=0,i=o.length;i>e;e++)(o[e].geometries||o[e].geometry||o[e].features)&&this.addData(o[e]);return this}var s=this.options;if(!s.filter||s.filter(t)){var a=n.GeoJSON.geometryToLayer(t,s.pointToLayer);return a.feature=t,a.defaultOptions=a.options,this.resetStyle(a),s.onEachFeature&&s.onEachFeature(t,a),this.addLayer(a)}},resetStyle:function(t){var e=this.options.style;e&&(n.Util.extend(t.options,t.defaultOptions),this._setLayerStyle(t,e))},setStyle:function(t){this.eachLayer(function(e){this._setLayerStyle(e,t)},this)},_setLayerStyle:function(t,e){"function"==typeof e&&(e=e(t.feature)),t.setStyle&&t.setStyle(e)}}),n.extend(n.GeoJSON,{geometryToLayer:function(t,e){var i,o,s,a,r,h="Feature"===t.type?t.geometry:t,l=h.coordinates,u=[];switch(h.type){case"Point":return i=this.coordsToLatLng(l),e?e(t,i):new n.Marker(i);case"MultiPoint":for(s=0,a=l.length;a>s;s++)i=this.coordsToLatLng(l[s]),r=e?e(t,i):new n.Marker(i),u.push(r);return new n.FeatureGroup(u);case"LineString":return o=this.coordsToLatLngs(l),new n.Polyline(o);case"Polygon":return o=this.coordsToLatLngs(l,1),new n.Polygon(o);case"MultiLineString":return o=this.coordsToLatLngs(l,1),new n.MultiPolyline(o);case"MultiPolygon":return o=this.coordsToLatLngs(l,2),new n.MultiPolygon(o);case"GeometryCollection":for(s=0,a=h.geometries.length;a>s;s++)r=this.geometryToLayer({geometry:h.geometries[s],type:"Feature",properties:t.properties},e),u.push(r);return new n.FeatureGroup(u);default:throw Error("Invalid GeoJSON object.")}},coordsToLatLng:function(t,e){var i=parseFloat(t[e?0:1]),o=parseFloat(t[e?1:0]);return new n.LatLng(i,o)},coordsToLatLngs:function(t,e,i){var n,o,s,a=[];for(o=0,s=t.length;s>o;o++)n=e?this.coordsToLatLngs(t[o],e-1,i):this.coordsToLatLng(t[o],i),a.push(n);return a}}),n.geoJson=function(t,e){return new n.GeoJSON(t,e)},n.DomEvent={addListener:function(t,e,o,s){var a,r,h,l=n.stamp(o),u="_leaflet_"+e+l;return t[u]?this:(a=function(e){return o.call(s||t,e||n.DomEvent._getEvent())},n.Browser.msTouch&&0===e.indexOf("touch")?this.addMsTouchListener(t,e,a,l):(n.Browser.touch&&"dblclick"===e&&this.addDoubleTapListener&&this.addDoubleTapListener(t,a,l),"addEventListener"in t?"mousewheel"===e?(t.addEventListener("DOMMouseScroll",a,!1),t.addEventListener(e,a,!1)):"mouseenter"===e||"mouseleave"===e?(r=a,h="mouseenter"===e?"mouseover":"mouseout",a=function(e){return n.DomEvent._checkMouse(t,e)?r(e):i},t.addEventListener(h,a,!1)):t.addEventListener(e,a,!1):"attachEvent"in t&&t.attachEvent("on"+e,a),t[u]=a,this))},removeListener:function(t,e,i){var o=n.stamp(i),s="_leaflet_"+e+o,a=t[s];if(a)return n.Browser.msTouch&&0===e.indexOf("touch")?this.removeMsTouchListener(t,e,o):n.Browser.touch&&"dblclick"===e&&this.removeDoubleTapListener?this.removeDoubleTapListener(t,o):"removeEventListener"in t?"mousewheel"===e?(t.removeEventListener("DOMMouseScroll",a,!1),t.removeEventListener(e,a,!1)):"mouseenter"===e||"mouseleave"===e?t.removeEventListener("mouseenter"===e?"mouseover":"mouseout",a,!1):t.removeEventListener(e,a,!1):"detachEvent"in t&&t.detachEvent("on"+e,a),t[s]=null,this},stopPropagation:function(t){return t.stopPropagation?t.stopPropagation():t.cancelBubble=!0,this},disableClickPropagation:function(t){for(var e=n.DomEvent.stopPropagation,i=n.Draggable.START.length-1;i>=0;i--)n.DomEvent.addListener(t,n.Draggable.START[i],e);return n.DomEvent.addListener(t,"click",e).addListener(t,"dblclick",e)},preventDefault:function(t){return t.preventDefault?t.preventDefault():t.returnValue=!1,this},stop:function(t){return n.DomEvent.preventDefault(t).stopPropagation(t)},getMousePosition:function(t,i){var o=e.body,s=e.documentElement,a=t.pageX?t.pageX:t.clientX+o.scrollLeft+s.scrollLeft,r=t.pageY?t.pageY:t.clientY+o.scrollTop+s.scrollTop,h=new n.Point(a,r);return i?h._subtract(n.DomUtil.getViewportOffset(i)):h},getWheelDelta:function(t){var e=0;return t.wheelDelta&&(e=t.wheelDelta/120),t.detail&&(e=-t.detail/3),e},_checkMouse:function(t,e){var i=e.relatedTarget;if(!i)return!0;try{for(;i&&i!==t;)i=i.parentNode}catch(n){return!1}return i!==t},_getEvent:function(){var e=t.event;if(!e)for(var i=arguments.callee.caller;i&&(e=i.arguments[0],!e||t.Event!==e.constructor);)i=i.caller;return e}},n.DomEvent.on=n.DomEvent.addListener,n.DomEvent.off=n.DomEvent.removeListener,n.Draggable=n.Class.extend({includes:n.Mixin.Events,statics:{START:n.Browser.touch?["touchstart","mousedown"]:["mousedown"],END:{mousedown:"mouseup",touchstart:"touchend",MSPointerDown:"touchend"},MOVE:{mousedown:"mousemove",touchstart:"touchmove",MSPointerDown:"touchmove"},TAP_TOLERANCE:15},initialize:function(t,e,i){this._element=t,this._dragStartTarget=e||t,this._longPress=i&&!n.Browser.msTouch},enable:function(){if(!this._enabled){for(var t=n.Draggable.START.length-1;t>=0;t--)n.DomEvent.on(this._dragStartTarget,n.Draggable.START[t],this._onDown,this);this._enabled=!0}},disable:function(){if(this._enabled){for(var t=n.Draggable.START.length-1;t>=0;t--)n.DomEvent.off(this._dragStartTarget,n.Draggable.START[t],this._onDown,this);this._enabled=!1,this._moved=!1}},_onDown:function(t){if(!(!n.Browser.touch&&t.shiftKey||1!==t.which&&1!==t.button&&!t.touches||(n.DomEvent.preventDefault(t),n.DomEvent.stopPropagation(t),n.Draggable._disabled))){if(this._simulateClick=!0,t.touches&&t.touches.length>1)return this._simulateClick=!1,clearTimeout(this._longPressTimeout),i;var o=t.touches&&1===t.touches.length?t.touches[0]:t,s=o.target;n.Browser.touch&&"a"===s.tagName.toLowerCase()&&n.DomUtil.addClass(s,"leaflet-active"),this._moved=!1,this._moving||(this._startPoint=new n.Point(o.clientX,o.clientY),this._startPos=this._newPos=n.DomUtil.getPosition(this._element),t.touches&&1===t.touches.length&&n.Browser.touch&&this._longPress&&(this._longPressTimeout=setTimeout(n.bind(function(){var t=this._newPos&&this._newPos.distanceTo(this._startPos)||0;n.Draggable.TAP_TOLERANCE>t&&(this._simulateClick=!1,this._onUp(),this._simulateEvent("contextmenu",o))},this),1e3)),n.DomEvent.on(e,n.Draggable.MOVE[t.type],this._onMove,this),n.DomEvent.on(e,n.Draggable.END[t.type],this._onUp,this))}},_onMove:function(t){if(!(t.touches&&t.touches.length>1)){var e=t.touches&&1===t.touches.length?t.touches[0]:t,i=new n.Point(e.clientX,e.clientY),o=i.subtract(this._startPoint);(o.x||o.y)&&(n.DomEvent.preventDefault(t),this._moved||(this.fire("dragstart"),this._moved=!0,this._startPos=n.DomUtil.getPosition(this._element).subtract(o),n.Browser.touch||(n.DomUtil.disableTextSelection(),this._setMovingCursor())),this._newPos=this._startPos.add(o),this._moving=!0,n.Util.cancelAnimFrame(this._animRequest),this._animRequest=n.Util.requestAnimFrame(this._updatePosition,this,!0,this._dragStartTarget))}},_updatePosition:function(){this.fire("predrag"),n.DomUtil.setPosition(this._element,this._newPos),this.fire("drag")},_onUp:function(t){var i;if(clearTimeout(this._longPressTimeout),this._simulateClick&&t.changedTouches){var o=t.changedTouches[0],s=o.target,a=this._newPos&&this._newPos.distanceTo(this._startPos)||0;"a"===s.tagName.toLowerCase()&&n.DomUtil.removeClass(s,"leaflet-active"),n.Draggable.TAP_TOLERANCE>a&&(i=o)}n.Browser.touch||(n.DomUtil.enableTextSelection(),this._restoreCursor());for(var r in n.Draggable.MOVE)n.Draggable.MOVE.hasOwnProperty(r)&&(n.DomEvent.off(e,n.Draggable.MOVE[r],this._onMove),n.DomEvent.off(e,n.Draggable.END[r],this._onUp));this._moved&&(n.Util.cancelAnimFrame(this._animRequest),this.fire("dragend")),this._moving=!1,i&&(this._moved=!1,this._simulateEvent("click",i))},_setMovingCursor:function(){n.DomUtil.addClass(e.body,"leaflet-dragging")},_restoreCursor:function(){n.DomUtil.removeClass(e.body,"leaflet-dragging")},_simulateEvent:function(i,n){var o=e.createEvent("MouseEvents");o.initMouseEvent(i,!0,!0,t,1,n.screenX,n.screenY,n.clientX,n.clientY,!1,!1,!1,!1,0,null),n.target.dispatchEvent(o)}}),n.Handler=n.Class.extend({initialize:function(t){this._map=t},enable:function(){this._enabled||(this._enabled=!0,this.addHooks())},disable:function(){this._enabled&&(this._enabled=!1,this.removeHooks())},enabled:function(){return!!this._enabled}}),n.Map.mergeOptions({dragging:!0,inertia:!n.Browser.android23,inertiaDeceleration:3400,inertiaMaxSpeed:1/0,inertiaThreshold:n.Browser.touch?32:18,easeLinearity:.25,longPress:!0,worldCopyJump:!1}),n.Map.Drag=n.Handler.extend({addHooks:function(){if(!this._draggable){var t=this._map;this._draggable=new n.Draggable(t._mapPane,t._container,t.options.longPress),this._draggable.on({dragstart:this._onDragStart,drag:this._onDrag,dragend:this._onDragEnd},this),t.options.worldCopyJump&&(this._draggable.on("predrag",this._onPreDrag,this),t.on("viewreset",this._onViewReset,this))}this._draggable.enable()},removeHooks:function(){this._draggable.disable()},moved:function(){return this._draggable&&this._draggable._moved},_onDragStart:function(){var t=this._map;t._panAnim&&t._panAnim.stop(),t.fire("movestart").fire("dragstart"),t.options.inertia&&(this._positions=[],this._times=[])},_onDrag:function(){if(this._map.options.inertia){var t=this._lastTime=+new Date,e=this._lastPos=this._draggable._newPos;this._positions.push(e),this._times.push(t),t-this._times[0]>200&&(this._positions.shift(),this._times.shift())}this._map.fire("move").fire("drag")},_onViewReset:function(){var t=this._map.getSize()._divideBy(2),e=this._map.latLngToLayerPoint(new n.LatLng(0,0));this._initialWorldOffset=e.subtract(t).x,this._worldWidth=this._map.project(new n.LatLng(0,180)).x},_onPreDrag:function(){var t=this._worldWidth,e=Math.round(t/2),i=this._initialWorldOffset,n=this._draggable._newPos.x,o=(n-e+i)%t+e-i,s=(n+e+i)%t-e-i,a=Math.abs(o+i)e.inertiaThreshold||!this._positions[0];if(o)t.fire("moveend");else{var s=this._lastPos.subtract(this._positions[0]),a=(this._lastTime+i-this._times[0])/1e3,r=e.easeLinearity,h=s.multiplyBy(r/a),l=h.distanceTo(new n.Point(0,0)),u=Math.min(e.inertiaMaxSpeed,l),c=h.multiplyBy(u/l),_=u/(e.inertiaDeceleration*r),d=c.multiplyBy(-_/2).round();n.Util.requestAnimFrame(function(){t.panBy(d,_,r)})}t.fire("dragend"),e.maxBounds&&n.Util.requestAnimFrame(this._panInsideMaxBounds,t,!0,t._container)},_panInsideMaxBounds:function(){this.panInsideBounds(this.options.maxBounds)}}),n.Map.addInitHook("addHandler","dragging",n.Map.Drag),n.Map.mergeOptions({doubleClickZoom:!0}),n.Map.DoubleClickZoom=n.Handler.extend({addHooks:function(){this._map.on("dblclick",this._onDoubleClick)},removeHooks:function(){this._map.off("dblclick",this._onDoubleClick)},_onDoubleClick:function(t){this.setView(t.latlng,this._zoom+1)}}),n.Map.addInitHook("addHandler","doubleClickZoom",n.Map.DoubleClickZoom),n.Map.mergeOptions({scrollWheelZoom:!0}),n.Map.ScrollWheelZoom=n.Handler.extend({addHooks:function(){n.DomEvent.on(this._map._container,"mousewheel",this._onWheelScroll,this),this._delta=0},removeHooks:function(){n.DomEvent.off(this._map._container,"mousewheel",this._onWheelScroll)},_onWheelScroll:function(t){var e=n.DomEvent.getWheelDelta(t);this._delta+=e,this._lastMousePos=this._map.mouseEventToContainerPoint(t),this._startTime||(this._startTime=+new Date);var i=Math.max(40-(+new Date-this._startTime),0);clearTimeout(this._timer),this._timer=setTimeout(n.bind(this._performZoom,this),i),n.DomEvent.preventDefault(t),n.DomEvent.stopPropagation(t)},_performZoom:function(){var t=this._map,e=this._delta,i=t.getZoom();if(e=e>0?Math.ceil(e):Math.round(e),e=Math.max(Math.min(e,4),-4),e=t._limitZoom(i+e)-i,this._delta=0,this._startTime=null,e){var n=i+e,o=this._getCenterForScrollWheelZoom(n);t.setView(o,n)}},_getCenterForScrollWheelZoom:function(t){var e=this._map,i=e.getZoomScale(t),n=e.getSize()._divideBy(2),o=this._lastMousePos._subtract(n)._multiplyBy(1-1/i),s=e._getTopLeftPoint()._add(n)._add(o);return e.unproject(s)}}),n.Map.addInitHook("addHandler","scrollWheelZoom",n.Map.ScrollWheelZoom),n.extend(n.DomEvent,{_touchstart:n.Browser.msTouch?"MSPointerDown":"touchstart",_touchend:n.Browser.msTouch?"MSPointerUp":"touchend",addDoubleTapListener:function(t,i,o){function s(t){var e;if(n.Browser.msTouch?(p.push(t.pointerId),e=p.length):e=t.touches.length,!(e>1)){var i=Date.now(),o=i-(r||i);h=t.touches?t.touches[0]:t,l=o>0&&u>=o,r=i}}function a(t){if(n.Browser.msTouch){var e=p.indexOf(t.pointerId);if(-1===e)return;p.splice(e,1)}if(l){if(n.Browser.msTouch){var o,s={};for(var a in h)o=h[a],s[a]="function"==typeof o?o.bind(h):o;h=s}h.type="dblclick",i(h),r=null}}var r,h,l=!1,u=250,c="_leaflet_",_=this._touchstart,d=this._touchend,p=[];t[c+_+o]=s,t[c+d+o]=a;var m=n.Browser.msTouch?e.documentElement:t;return t.addEventListener(_,s,!1),m.addEventListener(d,a,!1),n.Browser.msTouch&&m.addEventListener("MSPointerCancel",a,!1),this},removeDoubleTapListener:function(t,i){var o="_leaflet_";return t.removeEventListener(this._touchstart,t[o+this._touchstart+i],!1),(n.Browser.msTouch?e.documentElement:t).removeEventListener(this._touchend,t[o+this._touchend+i],!1),n.Browser.msTouch&&e.documentElement.removeEventListener("MSPointerCancel",t[o+this._touchend+i],!1),this}}),n.extend(n.DomEvent,{_msTouches:[],_msDocumentListener:!1,addMsTouchListener:function(t,e,i,n){switch(e){case"touchstart":return this.addMsTouchListenerStart(t,e,i,n);case"touchend":return this.addMsTouchListenerEnd(t,e,i,n);case"touchmove":return this.addMsTouchListenerMove(t,e,i,n);default:throw"Unknown touch event type"}},addMsTouchListenerStart:function(t,i,n,o){var s="_leaflet_",a=this._msTouches,r=function(t){for(var e=!1,i=0;a.length>i;i++)if(a[i].pointerId===t.pointerId){e=!0;break}e||a.push(t),t.touches=a.slice(),t.changedTouches=[t],n(t)};if(t[s+"touchstart"+o]=r,t.addEventListener("MSPointerDown",r,!1),!this._msDocumentListener){var h=function(t){for(var e=0;a.length>e;e++)if(a[e].pointerId===t.pointerId){a.splice(e,1);break}};e.documentElement.addEventListener("MSPointerUp",h,!1),e.documentElement.addEventListener("MSPointerCancel",h,!1),this._msDocumentListener=!0}return this},addMsTouchListenerMove:function(t,e,i,n){function o(t){if(t.pointerType!==t.MSPOINTER_TYPE_MOUSE||0!==t.buttons){for(var e=0;a.length>e;e++)if(a[e].pointerId===t.pointerId){a[e]=t;break}t.touches=a.slice(),t.changedTouches=[t],i(t)}}var s="_leaflet_",a=this._msTouches;return t[s+"touchmove"+n]=o,t.addEventListener("MSPointerMove",o,!1),this},addMsTouchListenerEnd:function(t,e,i,n){var o="_leaflet_",s=this._msTouches,a=function(t){for(var e=0;s.length>e;e++)if(s[e].pointerId===t.pointerId){s.splice(e,1);break}t.touches=s.slice(),t.changedTouches=[t],i(t)};return t[o+"touchend"+n]=a,t.addEventListener("MSPointerUp",a,!1),t.addEventListener("MSPointerCancel",a,!1),this},removeMsTouchListener:function(t,e,i){var n="_leaflet_",o=t[n+e+i];switch(e){case"touchstart":t.removeEventListener("MSPointerDown",o,!1);break;case"touchmove":t.removeEventListener("MSPointerMove",o,!1);break;case"touchend":t.removeEventListener("MSPointerUp",o,!1),t.removeEventListener("MSPointerCancel",o,!1)}return this}}),n.Map.mergeOptions({touchZoom:n.Browser.touch&&!n.Browser.android23}),n.Map.TouchZoom=n.Handler.extend({addHooks:function(){n.DomEvent.on(this._map._container,"touchstart",this._onTouchStart,this)},removeHooks:function(){n.DomEvent.off(this._map._container,"touchstart",this._onTouchStart,this)},_onTouchStart:function(t){var i=this._map;if(t.touches&&2===t.touches.length&&!i._animatingZoom&&!this._zooming){var o=i.mouseEventToLayerPoint(t.touches[0]),s=i.mouseEventToLayerPoint(t.touches[1]),a=i._getCenterLayerPoint();this._startCenter=o.add(s)._divideBy(2),this._startDist=o.distanceTo(s),this._moved=!1,this._zooming=!0,this._centerOffset=a.subtract(this._startCenter),i._panAnim&&i._panAnim.stop(),n.DomEvent.on(e,"touchmove",this._onTouchMove,this).on(e,"touchend",this._onTouchEnd,this),n.DomEvent.preventDefault(t)}},_onTouchMove:function(t){if(t.touches&&2===t.touches.length){var e=this._map,i=e.mouseEventToLayerPoint(t.touches[0]),o=e.mouseEventToLayerPoint(t.touches[1]);this._scale=i.distanceTo(o)/this._startDist,this._delta=i._add(o)._divideBy(2)._subtract(this._startCenter),1!==this._scale&&(this._moved||(n.DomUtil.addClass(e._mapPane,"leaflet-zoom-anim leaflet-touching"),e.fire("movestart").fire("zoomstart")._prepareTileBg(),this._moved=!0),n.Util.cancelAnimFrame(this._animRequest),this._animRequest=n.Util.requestAnimFrame(this._updateOnMove,this,!0,this._map._container),n.DomEvent.preventDefault(t))}},_updateOnMove:function(){var t=this._map,e=this._getScaleOrigin(),i=t.layerPointToLatLng(e);t.fire("zoomanim",{center:i,zoom:t.getScaleZoom(this._scale)}),t._tileBg.style[n.DomUtil.TRANSFORM]=n.DomUtil.getTranslateString(this._delta)+" "+n.DomUtil.getScaleString(this._scale,this._startCenter)},_onTouchEnd:function(){if(this._moved&&this._zooming){var t=this._map;this._zooming=!1,n.DomUtil.removeClass(t._mapPane,"leaflet-touching"),n.DomEvent.off(e,"touchmove",this._onTouchMove).off(e,"touchend",this._onTouchEnd);var i=this._getScaleOrigin(),o=t.layerPointToLatLng(i),s=t.getZoom(),a=t.getScaleZoom(this._scale)-s,r=a>0?Math.ceil(a):Math.floor(a),h=t._limitZoom(s+r);t.fire("zoomanim",{center:o,zoom:h}),t._runAnimation(o,h,t.getZoomScale(h)/this._scale,i,!0)}},_getScaleOrigin:function(){var t=this._centerOffset.subtract(this._delta).divideBy(this._scale);return this._startCenter.add(t)}}),n.Map.addInitHook("addHandler","touchZoom",n.Map.TouchZoom),n.Map.mergeOptions({boxZoom:!0}),n.Map.BoxZoom=n.Handler.extend({initialize:function(t){this._map=t,this._container=t._container,this._pane=t._panes.overlayPane},addHooks:function(){n.DomEvent.on(this._container,"mousedown",this._onMouseDown,this)},removeHooks:function(){n.DomEvent.off(this._container,"mousedown",this._onMouseDown)},_onMouseDown:function(t){return!t.shiftKey||1!==t.which&&1!==t.button?!1:(n.DomUtil.disableTextSelection(),this._startLayerPoint=this._map.mouseEventToLayerPoint(t),this._box=n.DomUtil.create("div","leaflet-zoom-box",this._pane),n.DomUtil.setPosition(this._box,this._startLayerPoint),this._container.style.cursor="crosshair",n.DomEvent.on(e,"mousemove",this._onMouseMove,this).on(e,"mouseup",this._onMouseUp,this).preventDefault(t),this._map.fire("boxzoomstart"),i)},_onMouseMove:function(t){var e=this._startLayerPoint,i=this._box,o=this._map.mouseEventToLayerPoint(t),s=o.subtract(e),a=new n.Point(Math.min(o.x,e.x),Math.min(o.y,e.y));n.DomUtil.setPosition(i,a),i.style.width=Math.max(0,Math.abs(s.x)-4)+"px",i.style.height=Math.max(0,Math.abs(s.y)-4)+"px"},_onMouseUp:function(t){this._pane.removeChild(this._box),this._container.style.cursor="",n.DomUtil.enableTextSelection(),n.DomEvent.off(e,"mousemove",this._onMouseMove).off(e,"mouseup",this._onMouseUp);var i=this._map,o=i.mouseEventToLayerPoint(t);if(!this._startLayerPoint.equals(o)){var s=new n.LatLngBounds(i.layerPointToLatLng(this._startLayerPoint),i.layerPointToLatLng(o));i.fitBounds(s),i.fire("boxzoomend",{boxZoomBounds:s})}}}),n.Map.addInitHook("addHandler","boxZoom",n.Map.BoxZoom),n.Map.mergeOptions({keyboard:!0,keyboardPanOffset:80,keyboardZoomOffset:1}),n.Map.Keyboard=n.Handler.extend({keyCodes:{left:[37],right:[39],down:[40],up:[38],zoomIn:[187,107,61],zoomOut:[189,109,173]},initialize:function(t){this._map=t,this._setPanOffset(t.options.keyboardPanOffset),this._setZoomOffset(t.options.keyboardZoomOffset)},addHooks:function(){var t=this._map._container;-1===t.tabIndex&&(t.tabIndex="0"),n.DomEvent.on(t,"focus",this._onFocus,this).on(t,"blur",this._onBlur,this).on(t,"mousedown",this._onMouseDown,this),this._map.on("focus",this._addHooks,this).on("blur",this._removeHooks,this)},removeHooks:function(){this._removeHooks();var t=this._map._container;n.DomEvent.off(t,"focus",this._onFocus,this).off(t,"blur",this._onBlur,this).off(t,"mousedown",this._onMouseDown,this),this._map.off("focus",this._addHooks,this).off("blur",this._removeHooks,this)},_onMouseDown:function(){this._focused||this._map._container.focus()},_onFocus:function(){this._focused=!0,this._map.fire("focus")},_onBlur:function(){this._focused=!1,this._map.fire("blur")},_setPanOffset:function(t){var e,i,n=this._panKeys={},o=this.keyCodes;for(e=0,i=o.left.length;i>e;e++)n[o.left[e]]=[-1*t,0];for(e=0,i=o.right.length;i>e;e++)n[o.right[e]]=[t,0];for(e=0,i=o.down.length;i>e;e++)n[o.down[e]]=[0,t];for(e=0,i=o.up.length;i>e;e++)n[o.up[e]]=[0,-1*t]},_setZoomOffset:function(t){var e,i,n=this._zoomKeys={},o=this.keyCodes;for(e=0,i=o.zoomIn.length;i>e;e++)n[o.zoomIn[e]]=t;for(e=0,i=o.zoomOut.length;i>e;e++)n[o.zoomOut[e]]=-t},_addHooks:function(){n.DomEvent.on(e,"keydown",this._onKeyDown,this)},_removeHooks:function(){n.DomEvent.off(e,"keydown",this._onKeyDown,this)},_onKeyDown:function(t){var e=t.keyCode,i=this._map;if(this._panKeys.hasOwnProperty(e))i.panBy(this._panKeys[e]),i.options.maxBounds&&i.panInsideBounds(i.options.maxBounds);else{if(!this._zoomKeys.hasOwnProperty(e))return;i.setZoom(i.getZoom()+this._zoomKeys[e])}n.DomEvent.stop(t)}}),n.Map.addInitHook("addHandler","keyboard",n.Map.Keyboard),n.Handler.MarkerDrag=n.Handler.extend({initialize:function(t){this._marker=t},addHooks:function(){var t=this._marker._icon;this._draggable||(this._draggable=new n.Draggable(t,t).on("dragstart",this._onDragStart,this).on("drag",this._onDrag,this).on("dragend",this._onDragEnd,this)),this._draggable.enable()},removeHooks:function(){this._draggable.disable()},moved:function(){return this._draggable&&this._draggable._moved},_onDragStart:function(){this._marker.closePopup().fire("movestart").fire("dragstart")},_onDrag:function(){var t=this._marker,e=t._shadow,i=n.DomUtil.getPosition(t._icon),o=t._map.layerPointToLatLng(i);e&&n.DomUtil.setPosition(e,i),t._latlng=o,t.fire("move",{latlng:o}).fire("drag")},_onDragEnd:function(){this._marker.fire("moveend").fire("dragend")}}),n.Handler.PolyEdit=n.Handler.extend({options:{icon:new n.DivIcon({iconSize:new n.Point(8,8),className:"leaflet-div-icon leaflet-editing-icon"})},initialize:function(t,e){this._poly=t,n.setOptions(this,e)},addHooks:function(){this._poly._map&&(this._markerGroup||this._initMarkers(),this._poly._map.addLayer(this._markerGroup))},removeHooks:function(){this._poly._map&&(this._poly._map.removeLayer(this._markerGroup),delete this._markerGroup,delete this._markers)},updateMarkers:function(){this._markerGroup.clearLayers(),this._initMarkers()},_initMarkers:function(){this._markerGroup||(this._markerGroup=new n.LayerGroup),this._markers=[];var t,e,i,o,s=this._poly._latlngs;for(t=0,i=s.length;i>t;t++)o=this._createMarker(s[t],t),o.on("click",this._onMarkerClick,this),this._markers.push(o);var a,r;for(t=0,e=i-1;i>t;e=t++)(0!==t||n.Polygon&&this._poly instanceof n.Polygon)&&(a=this._markers[e],r=this._markers[t],this._createMiddleMarker(a,r),this._updatePrevNext(a,r))},_createMarker:function(t,e){var i=new n.Marker(t,{draggable:!0,icon:this.options.icon});return i._origLatLng=t,i._index=e,i.on("drag",this._onMarkerDrag,this),i.on("dragend",this._fireEdit,this),this._markerGroup.addLayer(i),i},_fireEdit:function(){this._poly.fire("edit")},_onMarkerDrag:function(t){var e=t.target;n.extend(e._origLatLng,e._latlng),e._middleLeft&&e._middleLeft.setLatLng(this._getMiddleLatLng(e._prev,e)),e._middleRight&&e._middleRight.setLatLng(this._getMiddleLatLng(e,e._next)),this._poly.redraw()},_onMarkerClick:function(t){if(!(3>this._poly._latlngs.length)){var e=t.target,i=e._index;this._markerGroup.removeLayer(e),this._markers.splice(i,1),this._poly.spliceLatLngs(i,1),this._updateIndexes(i,-1),this._updatePrevNext(e._prev,e._next),e._middleLeft&&this._markerGroup.removeLayer(e._middleLeft),e._middleRight&&this._markerGroup.removeLayer(e._middleRight),e._prev&&e._next?this._createMiddleMarker(e._prev,e._next):e._prev?e._next||(e._prev._middleRight=null):e._next._middleLeft=null,this._poly.fire("edit")}},_updateIndexes:function(t,e){this._markerGroup.eachLayer(function(i){i._index>t&&(i._index+=e)})},_createMiddleMarker:function(t,e){var i,n,o,s=this._getMiddleLatLng(t,e),a=this._createMarker(s);a.setOpacity(.6),t._middleRight=e._middleLeft=a,n=function(){var n=e._index;a._index=n,a.off("click",i).on("click",this._onMarkerClick,this),s.lat=a.getLatLng().lat,s.lng=a.getLatLng().lng,this._poly.spliceLatLngs(n,0,s),this._markers.splice(n,0,a),a.setOpacity(1),this._updateIndexes(n,1),e._index++,this._updatePrevNext(t,a),this._updatePrevNext(a,e)},o=function(){a.off("dragstart",n,this),a.off("dragend",o,this),this._createMiddleMarker(t,a),this._createMiddleMarker(a,e)},i=function(){n.call(this),o.call(this),this._poly.fire("edit")},a.on("click",i,this).on("dragstart",n,this).on("dragend",o,this),this._markerGroup.addLayer(a)},_updatePrevNext:function(t,e){t&&(t._next=e),e&&(e._prev=t)},_getMiddleLatLng:function(t,e){var i=this._poly._map,n=i.latLngToLayerPoint(t.getLatLng()),o=i.latLngToLayerPoint(e.getLatLng());return i.layerPointToLatLng(n._add(o)._divideBy(2))}}),n.Polyline.addInitHook(function(){n.Handler.PolyEdit&&(this.editing=new n.Handler.PolyEdit(this),this.options.editable&&this.editing.enable()),this.on("add",function(){this.editing&&this.editing.enabled()&&this.editing.addHooks()}),this.on("remove",function(){this.editing&&this.editing.enabled()&&this.editing.removeHooks()})}),n.Control=n.Class.extend({options:{position:"topright"},initialize:function(t){n.setOptions(this,t)},getPosition:function(){return this.options.position},setPosition:function(t){var e=this._map;return e&&e.removeControl(this),this.options.position=t,e&&e.addControl(this),this
+},addTo:function(t){this._map=t;var e=this._container=this.onAdd(t),i=this.getPosition(),o=t._controlCorners[i];return n.DomUtil.addClass(e,"leaflet-control"),-1!==i.indexOf("bottom")?o.insertBefore(e,o.firstChild):o.appendChild(e),this},removeFrom:function(t){var e=this.getPosition(),i=t._controlCorners[e];return i.removeChild(this._container),this._map=null,this.onRemove&&this.onRemove(t),this}}),n.control=function(t){return new n.Control(t)},n.Map.include({addControl:function(t){return t.addTo(this),this},removeControl:function(t){return t.removeFrom(this),this},_initControlPos:function(){function t(t,s){var a=i+t+" "+i+s;e[t+s]=n.DomUtil.create("div",a,o)}var e=this._controlCorners={},i="leaflet-",o=this._controlContainer=n.DomUtil.create("div",i+"control-container",this._container);t("top","left"),t("top","right"),t("bottom","left"),t("bottom","right")}}),n.Control.Zoom=n.Control.extend({options:{position:"topleft"},onAdd:function(t){var e="leaflet-control-zoom",i="leaflet-bar",o=i+"-part",s=n.DomUtil.create("div",e+" "+i);return this._map=t,this._zoomInButton=this._createButton("+","Zoom in",e+"-in "+o+" "+o+"-top",s,this._zoomIn,this),this._zoomOutButton=this._createButton("-","Zoom out",e+"-out "+o+" "+o+"-bottom",s,this._zoomOut,this),t.on("zoomend",this._updateDisabled,this),s},onRemove:function(t){t.off("zoomend",this._updateDisabled,this)},_zoomIn:function(t){this._map.zoomIn(t.shiftKey?3:1)},_zoomOut:function(t){this._map.zoomOut(t.shiftKey?3:1)},_createButton:function(t,e,i,o,s,a){var r=n.DomUtil.create("a",i,o);r.innerHTML=t,r.href="#",r.title=e;var h=n.DomEvent.stopPropagation;return n.DomEvent.on(r,"click",h).on(r,"mousedown",h).on(r,"dblclick",h).on(r,"click",n.DomEvent.preventDefault).on(r,"click",s,a),r},_updateDisabled:function(){var t=this._map,e="leaflet-control-zoom-disabled";n.DomUtil.removeClass(this._zoomInButton,e),n.DomUtil.removeClass(this._zoomOutButton,e),t._zoom===t.getMinZoom()&&n.DomUtil.addClass(this._zoomOutButton,e),t._zoom===t.getMaxZoom()&&n.DomUtil.addClass(this._zoomInButton,e)}}),n.Map.mergeOptions({zoomControl:!0}),n.Map.addInitHook(function(){this.options.zoomControl&&(this.zoomControl=new n.Control.Zoom,this.addControl(this.zoomControl))}),n.control.zoom=function(t){return new n.Control.Zoom(t)},n.Control.Attribution=n.Control.extend({options:{position:"bottomright",prefix:'Powered by Leaflet '},initialize:function(t){n.setOptions(this,t),this._attributions={}},onAdd:function(t){return this._container=n.DomUtil.create("div","leaflet-control-attribution"),n.DomEvent.disableClickPropagation(this._container),t.on("layeradd",this._onLayerAdd,this).on("layerremove",this._onLayerRemove,this),this._update(),this._container},onRemove:function(t){t.off("layeradd",this._onLayerAdd).off("layerremove",this._onLayerRemove)},setPrefix:function(t){return this.options.prefix=t,this._update(),this},addAttribution:function(t){return t?(this._attributions[t]||(this._attributions[t]=0),this._attributions[t]++,this._update(),this):i},removeAttribution:function(t){return t?(this._attributions[t]--,this._update(),this):i},_update:function(){if(this._map){var t=[];for(var e in this._attributions)this._attributions.hasOwnProperty(e)&&this._attributions[e]&&t.push(e);var i=[];this.options.prefix&&i.push(this.options.prefix),t.length&&i.push(t.join(", ")),this._container.innerHTML=i.join(" — ")}},_onLayerAdd:function(t){t.layer.getAttribution&&this.addAttribution(t.layer.getAttribution())},_onLayerRemove:function(t){t.layer.getAttribution&&this.removeAttribution(t.layer.getAttribution())}}),n.Map.mergeOptions({attributionControl:!0}),n.Map.addInitHook(function(){this.options.attributionControl&&(this.attributionControl=(new n.Control.Attribution).addTo(this))}),n.control.attribution=function(t){return new n.Control.Attribution(t)},n.Control.Scale=n.Control.extend({options:{position:"bottomleft",maxWidth:100,metric:!0,imperial:!0,updateWhenIdle:!1},onAdd:function(t){this._map=t;var e="leaflet-control-scale",i=n.DomUtil.create("div",e),o=this.options;return this._addScales(o,e,i),t.on(o.updateWhenIdle?"moveend":"move",this._update,this),t.whenReady(this._update,this),i},onRemove:function(t){t.off(this.options.updateWhenIdle?"moveend":"move",this._update,this)},_addScales:function(t,e,i){t.metric&&(this._mScale=n.DomUtil.create("div",e+"-line",i)),t.imperial&&(this._iScale=n.DomUtil.create("div",e+"-line",i))},_update:function(){var t=this._map.getBounds(),e=t.getCenter().lat,i=6378137*Math.PI*Math.cos(e*Math.PI/180),n=i*(t.getNorthEast().lng-t.getSouthWest().lng)/180,o=this._map.getSize(),s=this.options,a=0;o.x>0&&(a=n*(s.maxWidth/o.x)),this._updateScales(s,a)},_updateScales:function(t,e){t.metric&&e&&this._updateMetric(e),t.imperial&&e&&this._updateImperial(e)},_updateMetric:function(t){var e=this._getRoundNum(t);this._mScale.style.width=this._getScaleWidth(e/t)+"px",this._mScale.innerHTML=1e3>e?e+" m":e/1e3+" km"},_updateImperial:function(t){var e,i,n,o=3.2808399*t,s=this._iScale;o>5280?(e=o/5280,i=this._getRoundNum(e),s.style.width=this._getScaleWidth(i/e)+"px",s.innerHTML=i+" mi"):(n=this._getRoundNum(o),s.style.width=this._getScaleWidth(n/o)+"px",s.innerHTML=n+" ft")},_getScaleWidth:function(t){return Math.round(this.options.maxWidth*t)-10},_getRoundNum:function(t){var e=Math.pow(10,(Math.floor(t)+"").length-1),i=t/e;return i=i>=10?10:i>=5?5:i>=3?3:i>=2?2:1,e*i}}),n.control.scale=function(t){return new n.Control.Scale(t)},n.Control.Layers=n.Control.extend({options:{collapsed:!0,position:"topright",autoZIndex:!0},initialize:function(t,e,i){n.setOptions(this,i),this._layers={},this._lastZIndex=0,this._handlingClick=!1;for(var o in t)t.hasOwnProperty(o)&&this._addLayer(t[o],o);for(o in e)e.hasOwnProperty(o)&&this._addLayer(e[o],o,!0)},onAdd:function(t){return this._initLayout(),this._update(),t.on("layeradd",this._onLayerChange,this).on("layerremove",this._onLayerChange,this),this._container},onRemove:function(t){t.off("layeradd",this._onLayerChange).off("layerremove",this._onLayerChange)},addBaseLayer:function(t,e){return this._addLayer(t,e),this._update(),this},addOverlay:function(t,e){return this._addLayer(t,e,!0),this._update(),this},removeLayer:function(t){var e=n.stamp(t);return delete this._layers[e],this._update(),this},_initLayout:function(){var t="leaflet-control-layers",e=this._container=n.DomUtil.create("div",t);n.Browser.touch?n.DomEvent.on(e,"click",n.DomEvent.stopPropagation):(n.DomEvent.disableClickPropagation(e),n.DomEvent.on(e,"mousewheel",n.DomEvent.stopPropagation));var i=this._form=n.DomUtil.create("form",t+"-list");if(this.options.collapsed){n.DomEvent.on(e,"mouseover",this._expand,this).on(e,"mouseout",this._collapse,this);var o=this._layersLink=n.DomUtil.create("a",t+"-toggle",e);o.href="#",o.title="Layers",n.Browser.touch?n.DomEvent.on(o,"click",n.DomEvent.stopPropagation).on(o,"click",n.DomEvent.preventDefault).on(o,"click",this._expand,this):n.DomEvent.on(o,"focus",this._expand,this),this._map.on("movestart",this._collapse,this)}else this._expand();this._baseLayersList=n.DomUtil.create("div",t+"-base",i),this._separator=n.DomUtil.create("div",t+"-separator",i),this._overlaysList=n.DomUtil.create("div",t+"-overlays",i),e.appendChild(i)},_addLayer:function(t,e,i){var o=n.stamp(t);this._layers[o]={layer:t,name:e,overlay:i},this.options.autoZIndex&&t.setZIndex&&(this._lastZIndex++,t.setZIndex(this._lastZIndex))},_update:function(){if(this._container){this._baseLayersList.innerHTML="",this._overlaysList.innerHTML="";var t=!1,e=!1;for(var i in this._layers)if(this._layers.hasOwnProperty(i)){var n=this._layers[i];this._addItem(n),e=e||n.overlay,t=t||!n.overlay}this._separator.style.display=e&&t?"":"none"}},_onLayerChange:function(t){var e=n.stamp(t.layer);this._layers[e]&&!this._handlingClick&&this._update()},_createRadioElement:function(t,i){var n=' ";var o=e.createElement("div");return o.innerHTML=n,o.firstChild},_addItem:function(t){var i,o=e.createElement("label"),s=this._map.hasLayer(t.layer);t.overlay?(i=e.createElement("input"),i.type="checkbox",i.className="leaflet-control-layers-selector",i.defaultChecked=s):i=this._createRadioElement("leaflet-base-layers",s),i.layerId=n.stamp(t.layer),n.DomEvent.on(i,"click",this._onInputClick,this);var a=e.createElement("span");a.innerHTML=" "+t.name,o.appendChild(i),o.appendChild(a);var r=t.overlay?this._overlaysList:this._baseLayersList;return r.appendChild(o),o},_onInputClick:function(){var t,e,i,n,o=this._form.getElementsByTagName("input"),s=o.length;for(this._handlingClick=!0,t=0;s>t;t++)e=o[t],i=this._layers[e.layerId],e.checked&&!this._map.hasLayer(i.layer)?(this._map.addLayer(i.layer),i.overlay||(n=i.layer)):!e.checked&&this._map.hasLayer(i.layer)&&this._map.removeLayer(i.layer);n&&(this._map.setZoom(this._map.getZoom()),this._map.fire("baselayerchange",{layer:n})),this._handlingClick=!1},_expand:function(){n.DomUtil.addClass(this._container,"leaflet-control-layers-expanded")},_collapse:function(){this._container.className=this._container.className.replace(" leaflet-control-layers-expanded","")}}),n.control.layers=function(t,e,i){return new n.Control.Layers(t,e,i)},n.PosAnimation=n.Class.extend({includes:n.Mixin.Events,run:function(t,e,i,o){this.stop(),this._el=t,this._inProgress=!0,this.fire("start"),t.style[n.DomUtil.TRANSITION]="all "+(i||.25)+"s cubic-bezier(0,0,"+(o||.5)+",1)",n.DomEvent.on(t,n.DomUtil.TRANSITION_END,this._onTransitionEnd,this),n.DomUtil.setPosition(t,e),n.Util.falseFn(t.offsetWidth),this._stepTimer=setInterval(n.bind(this.fire,this,"step"),50)},stop:function(){this._inProgress&&(n.DomUtil.setPosition(this._el,this._getPos()),this._onTransitionEnd(),n.Util.falseFn(this._el.offsetWidth))},_transformRe:/(-?[\d\.]+), (-?[\d\.]+)\)/,_getPos:function(){var e,i,o,s=this._el,a=t.getComputedStyle(s);return n.Browser.any3d?(o=a[n.DomUtil.TRANSFORM].match(this._transformRe),e=parseFloat(o[1]),i=parseFloat(o[2])):(e=parseFloat(a.left),i=parseFloat(a.top)),new n.Point(e,i,!0)},_onTransitionEnd:function(){n.DomEvent.off(this._el,n.DomUtil.TRANSITION_END,this._onTransitionEnd,this),this._inProgress&&(this._inProgress=!1,this._el.style[n.DomUtil.TRANSITION]="",clearInterval(this._stepTimer),this.fire("step").fire("end"))}}),n.Map.include({setView:function(t,e,i){e=this._limitZoom(e);var n=this._zoom!==e;if(this._loaded&&!i&&this._layers){this._panAnim&&this._panAnim.stop();var o=n?this._zoomToIfClose&&this._zoomToIfClose(t,e):this._panByIfClose(t);if(o)return clearTimeout(this._sizeTimer),this}return this._resetView(t,e),this},panBy:function(t,e,i){if(t=n.point(t),!t.x&&!t.y)return this;this._panAnim||(this._panAnim=new n.PosAnimation,this._panAnim.on({step:this._onPanTransitionStep,end:this._onPanTransitionEnd},this)),this.fire("movestart"),n.DomUtil.addClass(this._mapPane,"leaflet-pan-anim");var o=n.DomUtil.getPosition(this._mapPane).subtract(t)._round();return this._panAnim.run(this._mapPane,o,e||.25,i),this},_onPanTransitionStep:function(){this.fire("move")},_onPanTransitionEnd:function(){n.DomUtil.removeClass(this._mapPane,"leaflet-pan-anim"),this.fire("moveend")},_panByIfClose:function(t){var e=this._getCenterOffset(t)._floor();return this._offsetIsWithinView(e)?(this.panBy(e),!0):!1},_offsetIsWithinView:function(t,e){var i=e||1,n=this.getSize();return Math.abs(t.x)<=n.x*i&&Math.abs(t.y)<=n.y*i}}),n.PosAnimation=n.DomUtil.TRANSITION?n.PosAnimation:n.PosAnimation.extend({run:function(t,e,i,o){this.stop(),this._el=t,this._inProgress=!0,this._duration=i||.25,this._easeOutPower=1/Math.max(o||.5,.2),this._startPos=n.DomUtil.getPosition(t),this._offset=e.subtract(this._startPos),this._startTime=+new Date,this.fire("start"),this._animate()},stop:function(){this._inProgress&&(this._step(),this._complete())},_animate:function(){this._animId=n.Util.requestAnimFrame(this._animate,this),this._step()},_step:function(){var t=+new Date-this._startTime,e=1e3*this._duration;e>t?this._runFrame(this._easeOut(t/e)):(this._runFrame(1),this._complete())},_runFrame:function(t){var e=this._startPos.add(this._offset.multiplyBy(t));n.DomUtil.setPosition(this._el,e),this.fire("step")},_complete:function(){n.Util.cancelAnimFrame(this._animId),this._inProgress=!1,this.fire("end")},_easeOut:function(t){return 1-Math.pow(1-t,this._easeOutPower)}}),n.Map.mergeOptions({zoomAnimation:n.DomUtil.TRANSITION&&!n.Browser.android23&&!n.Browser.mobileOpera}),n.DomUtil.TRANSITION&&n.Map.addInitHook(function(){n.DomEvent.on(this._mapPane,n.DomUtil.TRANSITION_END,this._catchTransitionEnd,this)}),n.Map.include(n.DomUtil.TRANSITION?{_zoomToIfClose:function(t,e){if(this._animatingZoom)return!0;if(!this.options.zoomAnimation)return!1;var i=this.getZoomScale(e),o=this._getCenterOffset(t)._divideBy(1-1/i);if(!this._offsetIsWithinView(o,1))return!1;n.DomUtil.addClass(this._mapPane,"leaflet-zoom-anim"),this.fire("movestart").fire("zoomstart"),this.fire("zoomanim",{center:t,zoom:e});var s=this._getCenterLayerPoint().add(o);return this._prepareTileBg(),this._runAnimation(t,e,i,s),!0},_catchTransitionEnd:function(){this._animatingZoom&&this._onZoomTransitionEnd()},_runAnimation:function(t,e,i,o,s){this._animateToCenter=t,this._animateToZoom=e,this._animatingZoom=!0,n.Draggable&&(n.Draggable._disabled=!0);var a=n.DomUtil.TRANSFORM,r=this._tileBg;clearTimeout(this._clearTileBgTimer),n.Util.falseFn(r.offsetWidth);var h=n.DomUtil.getScaleString(i,o),l=r.style[a];r.style[a]=s?l+" "+h:h+" "+l},_prepareTileBg:function(){var t=this._tilePane,e=this._tileBg;if(e&&this._getLoadedTilesPercentage(e)>.5&&.5>this._getLoadedTilesPercentage(t))return t.style.visibility="hidden",t.empty=!0,this._stopLoadingImages(t),i;e||(e=this._tileBg=this._createPane("leaflet-tile-pane",this._mapPane),e.style.zIndex=1),e.style[n.DomUtil.TRANSFORM]="",e.style.visibility="hidden",e.empty=!0,t.empty=!1,this._tilePane=this._panes.tilePane=e;var o=this._tileBg=t;n.DomUtil.addClass(o,"leaflet-zoom-animated"),this._stopLoadingImages(o)},_getLoadedTilesPercentage:function(t){var e,i,n=t.getElementsByTagName("img"),o=0;for(e=0,i=n.length;i>e;e++)n[e].complete&&o++;return o/i},_stopLoadingImages:function(t){var e,i,o,s=Array.prototype.slice.call(t.getElementsByTagName("img"));for(e=0,i=s.length;i>e;e++)o=s[e],o.complete||(o.onload=n.Util.falseFn,o.onerror=n.Util.falseFn,o.src=n.Util.emptyImageUrl,o.parentNode.removeChild(o))},_onZoomTransitionEnd:function(){this._restoreTileFront(),n.DomUtil.removeClass(this._mapPane,"leaflet-zoom-anim"),n.Util.falseFn(this._tileBg.offsetWidth),this._animatingZoom=!1,this._resetView(this._animateToCenter,this._animateToZoom,!0,!0),n.Draggable&&(n.Draggable._disabled=!1)},_restoreTileFront:function(){this._tilePane.innerHTML="",this._tilePane.style.visibility="",this._tilePane.style.zIndex=2,this._tileBg.style.zIndex=1},_clearTileBg:function(){this._animatingZoom||this.touchZoom._zooming||(this._tileBg.innerHTML="")}}:{}),n.Map.include({_defaultLocateOptions:{watch:!1,setView:!1,maxZoom:1/0,timeout:1e4,maximumAge:0,enableHighAccuracy:!1},locate:function(t){if(t=this._locationOptions=n.extend(this._defaultLocateOptions,t),!navigator.geolocation)return this._handleGeolocationError({code:0,message:"Geolocation not supported."}),this;var e=n.bind(this._handleGeolocationResponse,this),i=n.bind(this._handleGeolocationError,this);return t.watch?this._locationWatchId=navigator.geolocation.watchPosition(e,i,t):navigator.geolocation.getCurrentPosition(e,i,t),this},stopLocate:function(){return navigator.geolocation&&navigator.geolocation.clearWatch(this._locationWatchId),this},_handleGeolocationError:function(t){var e=t.code,i=t.message||(1===e?"permission denied":2===e?"position unavailable":"timeout");this._locationOptions.setView&&!this._loaded&&this.fitWorld(),this.fire("locationerror",{code:e,message:"Geolocation error: "+i+"."})},_handleGeolocationResponse:function(t){var e=180*t.coords.accuracy/4e7,i=2*e,o=t.coords.latitude,s=t.coords.longitude,a=new n.LatLng(o,s),r=new n.LatLng(o-e,s-i),h=new n.LatLng(o+e,s+i),l=new n.LatLngBounds(r,h),u=this._locationOptions;if(u.setView){var c=Math.min(this.getBoundsZoom(l),u.maxZoom);this.setView(a,c)}this.fire("locationfound",{latlng:a,bounds:l,accuracy:t.coords.accuracy})}})})(this,document);
\ No newline at end of file
--
cgit v1.2.3
From 273c6c037a87c1ecff8bc0630b0254b74da1ac01 Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Mon, 1 Apr 2013 14:49:49 +0200
Subject: Change settings to allow different JS map renderer
---
chimere/settings.sample.py | 278 +++++++++++++++++++++
chimere/templates/chimere/blocks/head_chimere.html | 4 +-
chimere/templatetags/chimere_tags.py | 8 +-
chimere/widgets.py | 25 +-
4 files changed, 299 insertions(+), 16 deletions(-)
create mode 100644 chimere/settings.sample.py
diff --git a/chimere/settings.sample.py b/chimere/settings.sample.py
new file mode 100644
index 0000000..6db1464
--- /dev/null
+++ b/chimere/settings.sample.py
@@ -0,0 +1,278 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# Don't edit this file:
+# overload all theses settings in your local_settings.py file
+
+import os
+
+DEBUG = False
+TEMPLATE_DEBUG = DEBUG
+
+# Django settings for chimere project.
+PROJECT_NAME = 'Chimere'
+ROOT_PATH = os.path.realpath(os.path.dirname(__file__)) + "/"
+
+EMAIL_HOST = 'localhost'
+STATIC_URL = '/static/'
+STATIC_ROOT = ROOT_PATH + 'static/'
+
+TINYMCE_URL = '/tinymce/'
+JQUERY_JS_URLS = ('/javascript/jquery/jquery.js',
+ '/javascript/jquery-ui/jquery-ui.js',)
+JQUERY_CSS_URLS = ('/javascript/jquery-ui/css/smoothness/jquery-ui.css',
+ '/javascript/jquery-ui-themes/base/jquery.ui.all.css')
+
+GPSBABEL = '/usr/bin/gpsbabel'
+GPSBABEL_OPTIONS = 'simplify,crosstrack,error=0.005k' # simplify with an error of 5 meters
+#GPSBABEL_OPTIONS = 'simplify,count=100'
+
+## chimere specific ##
+CHIMERE_DEFAULT_ZOOM = 10
+# center of the map
+CHIMERE_DEFAULT_CENTER = (-1.679444, 48.114722)
+# projection used by the main map
+# most public map providers use spherical mercator : 900913
+CHIMERE_EPSG_PROJECTION = 900913
+# projection displayed to the end user by openlayers
+# chimere use the same projection to save its data in the database
+CHIMERE_EPSG_DISPLAY_PROJECTION = 4326
+# display of shortcuts for areas
+CHIMERE_DISPLAY_AREAS = True
+# number of day before an event to display
+# if equal to 0: disable event management
+# if you change this value from 0 to a value in a production environnement
+# don't forget to run the upgrade.py script to create appropriate fields in
+# the database
+CHIMERE_DAYS_BEFORE_EVENT = 30
+# allow feeds
+CHIMERE_FEEDS = True
+
+CHIMERE_ICON_WIDTH = 21
+CHIMERE_ICON_HEIGHT = 25
+CHIMERE_ICON_OFFSET_X = -10
+CHIMERE_ICON_OFFSET_Y = -25
+
+# display picture inside the description by default or inside a galery?
+CHIMERE_MINIATURE_BY_DEFAULT = False
+
+# JS definition of the default map (for admin and when no map are defined in
+# the application)
+# cf. OpenLayers documentation for more details
+CHIMERE_DEFAULT_MAP_LAYER = "new OpenLayers.Layer.OSM.Mapnik('Mapnik')" # OSM mapnik map
+
+CHIMERE_XAPI_URL = 'http://open.mapquestapi.com/xapi/api/0.6/'
+CHIMERE_OSM_API_URL = 'api06.dev.openstreetmap.org' # test URL
+CHIMERE_OSM_USER = 'test'
+CHIMERE_OSM_PASSWORD = 'test'
+
+# as the web server need to be reloaded when property models are changed
+# it could be a good idea to hide it to an admin who could'nt do that
+CHIMERE_HIDE_PROPERTYMODEL = False
+
+# encoding for shapefile import
+CHIMERE_SHAPEFILE_ENCODING = 'ISO-8859-1'
+
+# thumbnail
+CHIMERE_THUMBS_SCALE_HEIGHT=250
+CHIMERE_THUMBS_SCALE_WIDTH=None
+
+CHIMERE_CSV_ENCODING = 'ISO-8859-1'
+
+ADMINS = (
+ # ('Your Name', 'your_email@domain.com'),
+)
+
+MANAGERS = ADMINS
+
+DATABASES = {
+ 'default': {
+ 'NAME': 'ratatouille',
+ 'ENGINE': 'django.contrib.gis.db.backends.postgis',
+ 'HOST': 'localhost',
+ 'PORT': '5432',
+ 'USER': 'ratatouille',
+ 'PASSWORD': 'wiki',
+ },
+}
+
+# Local time zone for this installation. Choices can be found here:
+# http://www.postgresql.org/docs/8.1/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE
+# although not all variations may be possible on all operating systems.
+# If running in a Windows environment this must be set to the same as your
+# system time zone.
+TIME_ZONE = 'Europe/Paris'
+
+# Language code for this installation. All choices can be found here:
+# http://www.w3.org/TR/REC-html40/struct/dirlang.html#langcodes
+# http://blogs.law.harvard.edu/tech/stories/storyReader$15
+LANGUAGE_CODE = 'fr-fr'
+
+SITE_ID = 1
+
+# If you set this to False, Django will make some optimizations so as not
+# to load the internationalization machinery.
+USE_I18N = True
+USE_L10N = True
+
+# Absolute path to the directory that holds media.
+# Example: "/home/media/media.lawrence.com/"
+MEDIA_ROOT = ROOT_PATH + 'media/'
+
+# URL that handles the media served from MEDIA_ROOT.
+# Example: "http://media.lawrence.com"
+MEDIA_URL = '/media/'
+
+# List of callables that know how to import templates from various sources.
+TEMPLATE_LOADERS = (
+ 'django.template.loaders.filesystem.Loader',
+ 'django.template.loaders.app_directories.Loader',
+# 'django.template.loaders.eggs.load_template_source',
+)
+
+MIDDLEWARE_CLASSES = (
+ 'django.middleware.common.CommonMiddleware',
+ 'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django.middleware.doc.XViewMiddleware',
+ 'django.contrib.messages.middleware.MessageMiddleware'
+)
+
+ROOT_URLCONF = 'chimere_example_project.urls'
+
+TEMPLATE_DIRS = [
+ # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
+ # Always use forward slashes, even on Windows.
+ # Don't forget to use absolute paths, not relative paths.
+ ROOT_PATH + 'templates',
+ ROOT_PATH + '../chimere/templates',
+]
+
+TEMPLATE_CONTEXT_PROCESSORS = (
+ "django.contrib.auth.context_processors.auth",
+ "django.core.context_processors.debug",
+ "django.core.context_processors.i18n",
+ "django.core.context_processors.media",
+ "django.core.context_processors.static",
+ "django.contrib.messages.context_processors.messages",
+ "django.core.context_processors.request",
+)
+
+INSTALLED_APPS = [
+ 'django.contrib.auth',
+ 'django.contrib.admin',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.sites',
+ 'django.contrib.gis',
+ 'django.contrib.staticfiles',
+]
+
+# celery
+try:
+ import djcelery
+ import kombu
+ djcelery.setup_loader()
+ BROKER_URL = 'django://'
+ INSTALLED_APPS += ['kombu.transport.django',
+ 'djcelery']
+except ImportError:
+ # some import and export will not be available
+ pass
+
+INSTALLED_APPS += [
+ 'south',
+ 'chimere',
+# 'chimere.scripts', # activate it if you want to use old migration scripts
+]
+
+LOG_PATH = '/var/log/django/'
+
+CHIMERE_VIEW_RENDERER = 'openlayers' # 'openlayers' or 'leaflet'
+CHIMERE_EDIT_RENDERER = 'openlayers' # 'openlayers'
+
+try:
+ from local_settings import *
+except ImportError, e:
+ print 'Unable to load local_settings.py:', e
+
+if 'LOGGING' not in globals():
+ global LOGGING
+ LOGGING = {'version': 1,
+ 'disable_existing_loggers': False,
+ 'handlers': {
+ # Include the default Django email handler for errors
+ # This is what you'd get without configuring logging at all.
+ 'mail_admins': {
+ 'class': 'django.utils.log.AdminEmailHandler',
+ 'level': 'ERROR',
+ # But the emails are plain text by default - HTML is nicer
+ 'include_html': True,
+ },
+ # Log to a text file that can be rotated by logrotate
+ 'logfile': {
+ 'class': 'logging.handlers.WatchedFileHandler',
+ 'filename': LOG_PATH + 'chimere.log'
+ },
+ },
+ 'loggers': {
+ # Again, default Django configuration to email unhandled exceptions
+ 'django.request': {
+ 'handlers': ['mail_admins'],
+ 'level': 'ERROR',
+ 'propagate': True,
+ },
+ # Might as well log any errors anywhere else in Django
+ 'django': {
+ 'handlers': ['logfile'],
+ 'level': 'ERROR',
+ 'propagate': False,
+ },
+ # Your own app - this assumes all your logger names start with "myapp."
+ 'chimere': {
+ 'handlers': ['logfile'],
+ 'level': 'WARNING', # Or maybe INFO or DEBUG
+ 'propogate': False
+ },
+ },
+ }
+
+
+if 'CHIMERE_SHARE_NETWORKS' not in globals():
+ # after the locals to get the right STATIC_URL
+
+ # share with
+ global CHIMERE_SHARE_NETWORKS
+ CHIMERE_SHARE_NETWORKS = (
+ ("Email", 'mailto:?subject=%(text)s&body=%(url)s',
+ STATIC_URL + 'chimere/img/email.png'),
+ ("Facebook", 'http://www.facebook.com/sharer.php?t=%(text)s&u=%(url)s',
+ STATIC_URL + 'chimere/img/facebook.png'),
+ ("Twitter", 'http://twitter.com/home?status=%(text)s %(url)s',
+ STATIC_URL + 'chimere/img/twitter.png'),
+ ("Identi.ca", 'http://identi.ca/index.php?action=newnotice&status_textarea=%(text)s %(url)s',
+ STATIC_URL + 'chimere/img/identica.png'),
+ )
+
+if 'MAP_JS_URLS' not in globals():
+ global MAP_JS_URLS
+ MAP_JS_URLS = {
+ 'openlayers':[
+ STATIC_URL + "openlayers/OpenLayers.js",
+ STATIC_URL + "openlayers/SimplePanZoom.js",
+ "http://www.openstreetmap.org/openlayers/OpenStreetMap.js"],
+ 'leaflet':[
+ STATIC_URL + "leaflet/leaflet.js"
+ ]
+ }
+
+if 'MAP_CSS_URLS' not in globals():
+ global MAP_CSS_URLS
+ MAP_CSS_URLS = {
+ 'openlayers':["http://www.openlayers.org/api/theme/default/style.css"],
+ 'leaflet':[
+ STATIC_URL + "leaflet/leaflet.css",
+ STATIC_URL + "leaflet/leaflet.ie.css"
+ ]
+ }
+
diff --git a/chimere/templates/chimere/blocks/head_chimere.html b/chimere/templates/chimere/blocks/head_chimere.html
index 147d05b..8863f10 100644
--- a/chimere/templates/chimere/blocks/head_chimere.html
+++ b/chimere/templates/chimere/blocks/head_chimere.html
@@ -1,6 +1,6 @@
-{% for css_url in OSM_CSS_URLS %}
+{% for css_url in MAP_CSS_URLS %}
{% endfor %}
-{% for js_url in OSM_JS_URLS %}
+{% for js_url in MAP_JS_URLS %}
{% endfor %}
{% if routing %}{% endif %}
diff --git a/chimere/templatetags/chimere_tags.py b/chimere/templatetags/chimere_tags.py
index ed835a8..06e72ed 100644
--- a/chimere/templatetags/chimere_tags.py
+++ b/chimere/templatetags/chimere_tags.py
@@ -119,7 +119,7 @@ def head_jme(context):
return context_data
@register.inclusion_tag('chimere/blocks/head_chimere.html', takes_context=True)
-def head_chimere(context):
+def head_chimere(context, view=True):
"""
Create context and display head elements (js, css, etc.) for chimere.
"""
@@ -130,6 +130,8 @@ def head_chimere(context):
area = Area.objects.get(urn=area_name)
except ObjectDoesNotExist:
pass
+ map_renderer = settings.CHIMERE_VIEW_RENDERER if view \
+ else settings.CHIMERE_EDIT_RENDERER
context_data = {
"STATIC_URL": settings.STATIC_URL,
"MEDIA_URL": settings.MEDIA_URL,
@@ -141,8 +143,8 @@ def head_chimere(context):
"DEFAULT_CENTER": settings.CHIMERE_DEFAULT_CENTER,
"DEFAULT_ZOOM": settings.CHIMERE_DEFAULT_ZOOM,
"MAP_LAYER": settings.CHIMERE_DEFAULT_MAP_LAYER,
- "OSM_CSS_URLS": settings.OSM_CSS_URLS,
- "OSM_JS_URLS": settings.OSM_JS_URLS,
+ "MAP_CSS_URLS": settings.MAP_CSS_URLS[map_renderer],
+ "MAP_JS_URLS": settings.MAP_JS_URLS[map_renderer],
'routing':settings.CHIMERE_ENABLE_ROUTING
}
return context_data
diff --git a/chimere/widgets.py b/chimere/widgets.py
index 45161e1..f39248c 100644
--- a/chimere/widgets.py
+++ b/chimere/widgets.py
@@ -248,10 +248,11 @@ class PointChooserWidget(forms.TextInput):
"""
class Media:
css = {
- "all": settings.OSM_CSS_URLS + \
+ "all": settings.MAP_CSS_URLS[settings.CHIMERE_EDIT_RENDERER] + \
["%schimere/css/forms.css" % settings.STATIC_URL,]
}
- js = settings.OSM_JS_URLS + list(settings.JQUERY_JS_URLS) + \
+ js = settings.MAP_JS_URLS[settings.CHIMERE_EDIT_RENDERER] + \
+ list(settings.JQUERY_JS_URLS) + \
["%schimere/js/jquery.chimere.js" % settings.STATIC_URL]
def render(self, name, value, attrs=None, area_name=''):
@@ -340,10 +341,12 @@ class RouteChooserWidget(forms.TextInput):
Manage the edition of route on a map
"""
class Media:
- css = {"all": settings.OSM_CSS_URLS + \
- ["%schimere/css/forms.css" % settings.STATIC_URL,]
+ css = {
+ "all": settings.MAP_CSS_URLS[settings.CHIMERE_EDIT_RENDERER] + \
+ ["%schimere/css/forms.css" % settings.STATIC_URL,]
}
- js = settings.OSM_JS_URLS + list(settings.JQUERY_JS_URLS) + \
+ js = settings.MAP_JS_URLS[settings.CHIMERE_EDIT_RENDERER] + \
+ list(settings.JQUERY_JS_URLS) + \
["%schimere/js/jquery.chimere.js" % settings.STATIC_URL,
"%schimere/js/edit_route_map.js" % settings.STATIC_URL,
"%schimere/js/base.js" % settings.STATIC_URL,]
@@ -477,11 +480,11 @@ class AreaWidget(forms.TextInput):
"""
class Media:
css = {
- "all": settings.OSM_CSS_URLS + \
+ "all": settings.MAP_CSS_URLS[settings.CHIMERE_EDIT_RENDERER] + \
["%schimere/css/forms.css" % settings.STATIC_URL,]
}
- js = settings.OSM_JS_URLS + [
- "%schimere/js/edit_area.js" % settings.STATIC_URL,
+ js = settings.MAP_JS_URLS[settings.CHIMERE_EDIT_RENDERER] + \
+ ["%schimere/js/edit_area.js" % settings.STATIC_URL,
"%schimere/js/base.js" % settings.STATIC_URL,]
def get_bounding_box_from_value(self, value):
@@ -576,11 +579,11 @@ class ImportFiltrWidget(AreaWidget):
"""
class Media:
css = {
- "all": settings.OSM_CSS_URLS + \
+ "all": settings.MAP_CSS_URLS[settings.CHIMERE_EDIT_RENDERER] + \
["%schimere/css/forms.css" % settings.STATIC_URL,]
}
- js = settings.OSM_JS_URLS + [
- "%schimere/js/edit_area.js" % settings.STATIC_URL,
+ js = settings.MAP_JS_URLS[settings.CHIMERE_EDIT_RENDERER] + \
+ ["%schimere/js/edit_area.js" % settings.STATIC_URL,
"%schimere/js/base.js" % settings.STATIC_URL,]
def render(self, name, value, attrs=None):
--
cgit v1.2.3
From d1a9401ff23b5abdb44b889b663711e9625eea27 Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Mon, 1 Apr 2013 18:44:39 +0200
Subject: Better management of map JS files
---
chimere/settings.sample.py | 16 +-
chimere/static/chimere/js/jquery.chimere-ol.js | 1695 ++++++++++++++++++++
chimere/static/chimere/js/jquery.chimere.js | 1695 --------------------
chimere/templates/chimere/blocks/head_chimere.html | 2 -
chimere/templates/chimere/main_map.html | 1 -
chimere/widgets.py | 6 +-
6 files changed, 1707 insertions(+), 1708 deletions(-)
create mode 100644 chimere/static/chimere/js/jquery.chimere-ol.js
delete mode 100644 chimere/static/chimere/js/jquery.chimere.js
diff --git a/chimere/settings.sample.py b/chimere/settings.sample.py
index c8fdb91..2e5ad40 100644
--- a/chimere/settings.sample.py
+++ b/chimere/settings.sample.py
@@ -1,7 +1,8 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-# This file is an example of settings.py for a Chimère project
+# Don't edit this file:
+# overload all theses settings in your local_settings.py file
import os
_ = lambda s: s
@@ -68,13 +69,13 @@ CHIMERE_OSM_API_URL = 'api06.dev.openstreetmap.org' # test URL
CHIMERE_OSM_USER = 'test'
CHIMERE_OSM_PASSWORD = 'test'
+# encoding for shapefile import
+CHIMERE_SHAPEFILE_ENCODING = 'ISO-8859-1'
+
# as the web server need to be reloaded when property models are changed
# it could be a good idea to hide it to an admin who could'nt do that
CHIMERE_HIDE_PROPERTYMODEL = False
-# encoding for shapefile import
-CHIMERE_SHAPEFILE_ENCODING = 'ISO-8859-1'
-
# enable routing in Chimère
CHIMERE_ENABLE_ROUTING = False
@@ -288,9 +289,11 @@ if 'MAP_JS_URLS' not in globals():
'openlayers':[
STATIC_URL + "openlayers/OpenLayers.js",
STATIC_URL + "openlayers/SimplePanZoom.js",
- "http://www.openstreetmap.org/openlayers/OpenStreetMap.js"],
+ "http://www.openstreetmap.org/openlayers/OpenStreetMap.js",
+ STATIC_URL + "chimere/js/jquery.chimere-ol.js"],
'leaflet':[
- STATIC_URL + "leaflet/leaflet.js"
+ STATIC_URL + "leaflet/leaflet.js",
+ STATIC_URL + "chimere/js/jquery.chimere-leaflet.js"
]
}
@@ -303,3 +306,4 @@ if 'MAP_CSS_URLS' not in globals():
STATIC_URL + "leaflet/leaflet.ie.css"
]
}
+
diff --git a/chimere/static/chimere/js/jquery.chimere-ol.js b/chimere/static/chimere/js/jquery.chimere-ol.js
new file mode 100644
index 0000000..2e2d238
--- /dev/null
+++ b/chimere/static/chimere/js/jquery.chimere-ol.js
@@ -0,0 +1,1695 @@
+/* Copyright (C) 2008-2012 Étienne Loks
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+
+See the file COPYING for details.
+*/
+
+/* Add OpenLayers MapQuest layer management */
+OpenLayers.Layer.MapQuestOSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {
+ name: "MapQuestOSM",
+ sphericalMercator: true,
+ url: ' http://otile1.mqcdn.com/tiles/1.0.0/osm/${z}/${x}/${y}.png',
+ clone: function(obj) {
+ if (obj == null) {
+ obj = new OpenLayers.Layer.OSM(
+ this.name, this.url, this.getOptions());
+ }
+ obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);
+ return obj;
+ },
+ CLASS_NAME: "OpenLayers.Layer.MapQuestOSM"
+});
+
+/*
+* Little hasattr helper
+*/
+(function ($) {
+ $.hasattr = function (key, arr) {
+ var v = arr[key];
+ if (typeof v === "undefined")
+ return false;
+ else
+ return v; };
+})( jQuery );
+
+(function ($) {
+ /*
+ * Chimere jQuery plugin
+ */
+ /*
+ * Default settings
+ */
+ var defaults = {
+ restricted_extent: false,
+ permalink_label: null,
+ permalink_div: null,
+ permalink: null, // OL Control, could be overrided
+ map_layers: null,
+ selected_map_layer: null,
+ dynamic_categories: false,
+ display_submited: false,
+ display_feature: null,
+ display_route: null,
+ area_id: null,
+ checked_categories: [],
+ zoom: null,
+ lat: null,
+ lon: null,
+ simple: false,
+ routing_start_lat: null,
+ routing_start_lon: null,
+ routing_end_lat: null,
+ routing_end_lon: null,
+ routing_steps_lonlat: null,
+ // Provide this function to make a custom click event on the marker
+ on_marker_click: null,
+ // Provide this function to override the feature detail display
+ display_feature_detail_fx: null,
+ // Provide this function for overriding the getSubcategories default
+ get_subcategories_fx: null,
+ hide_popup_fx: null,
+ // if leave to false every click on the map hide the pop-up
+ explicit_popup_hide: false,
+ controls:[new OpenLayers.Control.Navigation(),
+ new OpenLayers.Control.PanPanel(),
+ new OpenLayers.Control.ZoomPanel(),
+ new OpenLayers.Control.ScaleLine()],
+ popupClass: OpenLayers.Popup.FramedCloud,
+ popupContentFull: false, // if true the detail is inside the popup
+ category_accordion: true, // category opening behave like an accordion
+ maxResolution: 156543.0399,
+ units: 'm',
+ projection: new OpenLayers.Projection('EPSG:4326'),
+ theme: null,
+ enable_clustering: false,
+ routing: false, // enable routing management
+ routing_panel_open: function(){
+ $('#chimere_itinerary_panel').dialog('open');
+ },
+ current_feature: null, // To store the active POI
+ current_control: null, // To store the current control
+ current_popup: null, // To store the current POI popup displayed
+ current_category: null, // To store the current category clicked in list
+ current_route_feature: null, // To store the current route find by routing
+ itinerary_step_number:0, // current step number
+ icon_offset: new OpenLayers.Pixel(0, 0),
+ edition: false, // edition mode
+ edition_type_is_route: false, // route or POI edition
+ default_icon: new OpenLayers.Icon(
+ 'http://www.openlayers.org/dev/img/marker-green.png',
+ new OpenLayers.Size(21, 25),
+ new OpenLayers.Pixel(-(21/2), -25)),
+ cluster_icon: null,
+ marker_hover_id:'marker_hover',
+ marker_hover_content_id:'marker_hover_content',
+ marker_hover_offset: null,
+ icon_start: null,
+ icon_step: null,
+ icon_end: null
+ };
+ var settings = {};
+ /*
+ * Publics methods
+ */
+ var methods = {
+ /*
+ * Plugin init function
+ */
+ init: function ( options ) {
+ /* Manage parameters */
+ // not staticaly in default because of STATIC_URL init
+ if (defaults.cluster_icon == null && typeof STATIC_URL != 'undefined'){
+ defaults.cluster_icon = new OpenLayers.Icon(
+ STATIC_URL+'chimere/img/marker-cluster.png',
+ new OpenLayers.Size(36, 39),
+ new OpenLayers.Pixel(-(36/2), -(39/2)));
+ }
+ if (defaults.icon_start == null && typeof STATIC_URL != 'undefined'){
+ defaults.icon_start = new OpenLayers.Icon(
+ STATIC_URL + "chimere/img/flag-start.png",
+ new OpenLayers.Size(32, 32),
+ new OpenLayers.Pixel(0, -32));
+ }
+ if (defaults.icon_step == null && typeof STATIC_URL != 'undefined'){
+ defaults.icon_step = new OpenLayers.Icon(
+ STATIC_URL + "chimere/img/flag-step.png",
+ new OpenLayers.Size(32, 32),
+ new OpenLayers.Pixel(0, -32));
+ }
+ if (defaults.icon_end == null && typeof STATIC_URL != 'undefined'){
+ defaults.icon_end = new OpenLayers.Icon(
+ STATIC_URL + "chimere/img/flag-finish.png",
+ new OpenLayers.Size(32, 32),
+ new OpenLayers.Pixel(0, -32));
+ }
+ settings = $.extend({}, defaults);
+ if ( options ) $.extend(settings, options);
+ var map_element = $(this).get(0);
+ var map_options = {
+ controls: settings.controls,
+ maxResolution: settings.maxResolution,
+ units: settings.units,
+ projection: settings.projection,
+ theme: settings.theme
+ };
+ if (settings.restricted_extent){
+ settings.restricted_extent.transform(EPSG_DISPLAY_PROJECTION,
+ EPSG_PROJECTION);
+ map_options['restrictedExtent'] = settings.restricted_extent;
+ }
+
+ settings.current_position = null;
+
+ /* Create map object */
+ settings.map = map = new OpenLayers.Map(map_element, map_options);
+
+ map.addControl(new OpenLayers.Control.Attribution());
+
+ /* Manage permalink */
+ if (!settings.edition){
+ if (settings.permalink == null && !settings.edition) {
+ var permalink_options = {};
+ if (settings.permalink_element){
+ // hard to understand from OL documentation...
+ permalink_options["div"] = settings.permalink_element;
+ }
+ settings.permalink = new OpenLayers.Control.Permalink(
+ permalink_options);
+ }
+ /* HACK new permalink createParams method
+ FIXME when facilities are given to personalize the permalink */
+ var oldCreateParams = settings.permalink.createParams
+ var _createParams = function(center, zoom, layers) {
+ // Call normal method
+ var params = oldCreateParams(center, zoom, layers);
+ // Make specific params
+ params.checked_categories = settings.checked_categories;
+ params.display_submited = settings.display_submited;
+ if(settings.current_feature)
+ params.current_feature = settings.current_feature.pk;
+ if (settings.routing_start){
+ lonlat = settings.routing_start.lonlat.clone().transform(
+ EPSG_PROJECTION, EPSG_DISPLAY_PROJECTION);
+ params.routing_start_lon = lonlat.lon;
+ params.routing_start_lat = lonlat.lat;
+ }
+ if (settings.routing_end){
+ lonlat = settings.routing_end.lonlat.clone().transform(
+ EPSG_PROJECTION, EPSG_DISPLAY_PROJECTION);
+ params.routing_end_lon = lonlat.lon;
+ params.routing_end_lat = lonlat.lat;
+ }
+ if (settings.routing_steps){
+ var steps = [];
+ for (var i = 0; i < settings.routing_steps.length; i++){
+ lonlat = settings.routing_steps[i].lonlat.clone(
+ ).transform(EPSG_PROJECTION,
+ EPSG_DISPLAY_PROJECTION);
+ steps.push([lonlat.lon, lonlat.lat]);
+ }
+ params.routing_steps = steps;
+ }
+ return params;
+ }
+ // Force new createParams method
+ settings.permalink.createParams = _createParams;
+ settings.map.addControl(settings.permalink);
+ // update with the translated permalink label
+ if(settings.permalink_label && settings.permalink.div
+ && settings.permalink.div.childNodes.length > 0){
+ settings.permalink.div.childNodes[0].textContent = settings.permalink_label;
+ }
+ }
+ /* Add Layers */
+ settings.map.addLayers(settings.map_layers);
+ if (settings.map_layers.length > 1){
+ settings.map.addControl(new OpenLayers.Control.LayerSwitcher(
+ {roundedCorner:false}));
+ }
+ /* select the default map layer */
+ if (!settings.selected_map_layer){
+ settings.selected_map_layer = 0;
+ }
+ settings.map.setBaseLayer(
+ settings.map_layers[settings.selected_map_layer]);
+
+ /* manage the context menu */
+ $('#map_menu_zoomin').bind("click", methods.zoomIn);
+ $('#map_menu_zoomout').bind("click", methods.zoomOut);
+ $('#map_menu_center').bind("click", methods.mapCenter);
+ /* manage the routing */
+ if (settings.routing){
+ settings.routing_start = null;
+ settings.routing_steps = new Array();
+ settings.routing_end = null;
+ $('#map_menu_from').bind("click", methods.routingFrom);
+ $('#map_menu_step').bind("click", methods.routingAddStep);
+ $('#map_menu_to').bind("click", methods.routingTo);
+ $('#map_menu_clear').bind("click", methods.routingClear);
+ settings.layerRoute = new OpenLayers.Layer.Vector("Route Layer");
+ settings.map.addLayer(settings.layerRoute);
+ settings.layerRouteMarker = new OpenLayers.Layer.Markers(
+ 'Route markers');
+ settings.map.addLayer(settings.layerRouteMarker);
+ }
+ /* Vectors layer */
+ settings.layerVectors = new OpenLayers.Layer.Vector("Vector Layer");
+ settings.map.addLayer(settings.layerVectors);
+ settings.layerVectors.setOpacity(0.8);
+ if (settings.edition_type_is_route){
+ settings.map.addControl(new OpenLayers.Control.DrawFeature(
+ settings.layerVectors, OpenLayers.Handler.Path));
+ settings.map.addControl(new OpenLayers.Control.ModifyFeature(
+ settings.layerVectors, {clickout:false, toggle:false}));
+ }
+
+ if (settings.enable_clustering){
+ var style = new OpenLayers.Style({
+ graphicTitle: "${name}",
+ externalGraphic: "${icon}",
+ graphicWidth: "${width}",
+ graphicHeight: "${height}",
+ graphicXOffset: "${offsetx}",
+ graphicYOffset: "${offsety}",
+ graphicOpacity: 1,
+ label: "${label}",
+ labelYOffset: "2",
+ fontSize:'1.3em'
+ }, {
+ context: {
+ name: function(feature) {
+ if(feature.cluster) {
+ feature.attributes.width = settings.cluster_icon.size.w;
+ feature.attributes.height = settings.cluster_icon.size.h;
+ feature.attributes.offsetx = settings.cluster_icon.offset.x;
+ feature.attributes.offsety = settings.cluster_icon.offset.y;
+ } else{
+ var marker = feature.attributes.marker
+ feature.attributes.width = marker.icon.size.w;
+ feature.attributes.height = marker.icon.size.h;
+ feature.attributes.offsetx = settings.icon_offset.x;
+ feature.attributes.offsety = settings.icon_offset.y;
+ }
+ return feature.attributes.name;
+ },
+ label: function(feature) {
+ // clustered features count or blank if feature is not a cluster
+ return feature.cluster ? feature.cluster.length : "";
+ },
+ icon: function(feature) {
+ if (feature.cluster){
+ return settings.cluster_icon.url;
+ } else {
+ return STATIC_URL + 'chimere/img/empty.png';
+ }
+ },
+ width: function(feature) { return feature.attributes.width; },
+ height: function(feature) { return feature.attributes.height; },
+ offsetx: function(feature) { return feature.attributes.offsetx; },
+ offsety: function(feature) { return feature.attributes.offsety; }
+ }});
+
+
+ /* Cluster layer */
+ settings.clustering = new OpenLayers.Strategy.Cluster({
+ distance: 10,
+ threshold: 2});
+ settings.layerCluster = new OpenLayers.Layer.Vector("Cluster layer",
+ {styleMap: new OpenLayers.StyleMap({'default': style}),
+ strategies: [settings.clustering]});
+ settings.map.addLayer(settings.layerCluster);
+
+ var highlightCtrl = new OpenLayers.Control.SelectFeature(
+ settings.layerCluster, {
+ hover: true,
+ highlightOnly: true,
+ eventListeners: {
+ featurehighlighted: function(e) {
+ if(e.feature.attributes.marker)
+ e.feature.attributes.marker.events.triggerEvent('mouseover');
+ },
+ featureunhighlighted: function(e) {
+ if(e.feature.attributes.marker)
+ e.feature.attributes.marker.events.triggerEvent('mouseout');
+ }
+ }
+ });
+
+ var selectCtrl = new OpenLayers.Control.SelectFeature(
+ settings.layerCluster,{
+ onSelect: methods.zoomOnCluster
+ });
+
+ settings.map.addControl(highlightCtrl);
+ settings.map.addControl(selectCtrl);
+
+ highlightCtrl.activate();
+ selectCtrl.activate();
+ }
+
+ /* Markers layer */
+ settings.layerMarkers = new OpenLayers.Layer.Markers('POIs');
+ settings.map.addLayer(settings.layerMarkers);
+
+ if (settings.dynamic_categories){
+ settings.map.events.register('moveend', settings.map,
+ methods.loadCategories);
+ }
+ /* TODO make a function */
+ if (settings.display_submited) {
+ document.getElementById('display_submited_check').checked = true;
+ }
+ /* if we have some zoom and lon/lat from the init options */
+ if (settings.zoom && settings.lon && settings.lat) {
+ var centerLonLat = new OpenLayers.LonLat(settings.lon,
+ settings.lat);
+ settings.map.setCenter(centerLonLat, settings.zoom);
+ }
+ /* if not zoom to the extent in cookies */
+ else if (!methods.zoomToCurrentExtent(settings.map)){
+ /* if no extent in cookies zoom to default */
+ if(CENTER_LONLAT && DEFAULT_ZOOM){
+ settings.map.setCenter(CENTER_LONLAT, DEFAULT_ZOOM);
+ }
+ }
+
+ if (!settings.edition){
+ if (settings.enable_clustering){
+ settings.map.events.register('zoomend', null,
+ methods.cleanCluster);
+ }
+ methods.loadCategories();
+ methods.loadGeoObjects();
+ methods.activateContextMenu()
+ } else {
+ if (!settings.edition_type_is_route){
+ methods.activateMarkerEdit();
+ } else {
+ settings.layerVectors.events.register('featuremodified',
+ settings.layerVectors, helpers.updateRouteForm);
+ settings.layerVectors.events.register('featureadded',
+ settings.layerVectors, helpers.featureRouteCreated);
+ }
+ }
+ if (settings.routing_start_lon && settings.routing_start_lat){
+ settings.current_position = new OpenLayers.LonLat(
+ settings.routing_start_lon, settings.routing_start_lat
+ ).transform(EPSG_DISPLAY_PROJECTION, EPSG_PROJECTION);
+ methods.routingFrom();
+ }
+ if (settings.routing_end_lon && settings.routing_end_lat){
+ settings.current_position = new OpenLayers.LonLat(
+ settings.routing_end_lon, settings.routing_end_lat
+ ).transform(EPSG_DISPLAY_PROJECTION, EPSG_PROJECTION);
+ methods.routingTo();
+ }
+ if (settings.routing_steps_lonlat){
+ for (var i = 0; i < settings.routing_steps_lonlat.length/2; i++) {
+ lon = settings.routing_steps_lonlat[i*2];
+ lat = settings.routing_steps_lonlat[i*2+1];
+ settings.current_position = new OpenLayers.LonLat(lon, lat
+ ).transform(EPSG_DISPLAY_PROJECTION, EPSG_PROJECTION);
+ methods.routingAddStep();
+ }
+ }
+ methods.preload_images();
+ }, // end of init
+ /* Preload icons */
+ preload_images: function(){
+ if (typeof extra_url == 'undefined') return;
+ var uri = extra_url + "getAllCategories/";
+ $.ajax({url: uri,
+ dataType: "json",
+ success: function (data) {
+ if (!data.categories){return}
+ for(var idx=0; idx span");
+ if (parent.find('input[type=checkbox]:checked').length){
+ parent_label.addClass('category-selected');
+ } else {
+ parent_label.removeClass('category-selected');
+ }
+ var master_check = parent.find("> input");
+ if (parent.find('.subcategories input[type=checkbox]').length ==
+ parent.find('.subcategories input[type=checkbox]:checked').length){
+ master_check.attr('checked', 'checked');
+ } else {
+ master_check.removeAttr('checked');
+ }
+
+ if($('#action-categories').length){
+ if ($('#categories input[type=checkbox]:checked').length){
+ $('#action-categories').addClass('category-selected');
+ } else {
+ $('#action-categories').removeClass('category-selected');
+ }
+ }
+ return master_check;
+ };
+ var _init_categories = function () {
+ /*
+ * Add event listener in categories DOM elements
+ */
+ $('#categories #ul_categories > li > input').bind("click",
+ function (e) {
+ methods.hidePopup(e);
+ _toggle_subcategories($(this));
+ methods.loadGeoObjects();
+ settings.permalink.updateLink();
+ });
+ $('.subcategories li input').bind("click", function (e) {
+ var c_name = $(this).attr('name');
+ c_name = c_name.substr(c_name.lastIndexOf("_")+1);
+ if($(this).is(':checked')){
+ methods.subcategory_detail(c_name);
+ }
+ var par = $(this).parent();
+ if ($(this).attr('checked')){
+ par.addClass('selected');
+ } else {
+ par.removeClass('selected');
+ }
+ methods.hidePopup(e);
+ methods.loadGeoObjects();
+ _toggle_categories($(this));
+ settings.permalink.updateLink();
+ if ($('#layer_cat_'+c_name).length){
+ $('#layer_cat_'+c_name).prop("checked",
+ this.checked);
+ }
+ });
+ $('#display_submited_check').bind("click", function () {
+ methods.loadGeoObjects();
+ settings.permalink.updateLink();
+ });
+ // Zoom to category
+ $(".zoom_to_category").bind("click", function (e) {
+ var id = this.id.substr(this.id.lastIndexOf("_")+1);
+ helpers.zoom_to_category(id);
+ });
+ $(".zoom_to_subcategory").bind("click", function (e) {
+ var id = this.id.substr(this.id.lastIndexOf("_")+1);
+ helpers.zoom_to_subcategories([id]);
+ });
+ $(".toggle_category").parent().bind("click", function (e) {
+ var item = $(this).children('.toggle_category');
+ var id = item.attr('id').substr(item.attr('id').lastIndexOf("_")+1);
+ methods.toggle_category(id);
+ });
+ }
+ var _reCheckCategories = function (){
+ /* recheck categories on init or when a redraw occurs */
+ if (!settings.checked_categories){
+ return;
+ }
+ $('#frm_categories .subcategories input:checkbox').each(function(index){
+ cat_id = $(this).attr('id').split('_').pop();
+ if (settings.checked_categories.indexOf(parseInt(cat_id)) != -1) {
+ $(this).attr("checked", "checked");
+ _toggle_categories($(this));
+ methods.toggle_category();
+ } else {
+ $(this).attr("checked", false);
+ }
+ });
+ if (settings.display_submited == true){
+ $('#display_submited_check').attr("checked", "checked");
+ }
+ }
+ },
+ /*
+ * Hide clusterized markers
+ */
+ cleanCluster: function (){
+ if (settings.map.getZoom() === 18) {
+ // Don't cluster at this level. No matter what.
+ settings.clustering.threshold = 1000;
+ } else {
+ settings.clustering.threshold = 2;
+ }
+ //settings.layerCluster.refresh({force:true});
+ settings.clustering.recluster();
+ var hidden_feature_idx = [];
+ if (settings.map.getZoom() != 18) {
+ for(var idx=0; idx -1){
+ settings.layerMarkers.markers[j].display(false);
+ } else {
+ settings.layerMarkers.markers[j].display(true);
+ }
+ }
+ },
+ /*
+ * Put a marker on the map
+ */
+ addMarker: function (mark) {
+ /*
+ * Default Feature configuration
+ * This can be overrided in on_marker_click, using settings.current_feature
+ */
+ var lat = mark.geometry.coordinates[1];
+ var lon = mark.geometry.coordinates[0];
+ var size = new OpenLayers.Size(mark.properties.icon_width,
+ mark.properties.icon_height);
+ var icon_url = MEDIA_URL + mark.properties.icon_path;
+ var icon_hover_url = '';
+ if (mark.properties.icon_hover_path){
+ var icon_hover_url = MEDIA_URL + mark.properties.icon_hover_path;
+ }
+ var iconclone = new OpenLayers.Icon(icon_url, size,
+ settings.icon_offset);
+
+ var feature = new OpenLayers.Feature(settings.layerMarkers,
+ new OpenLayers.LonLat(lon, lat).transform(
+ EPSG_DISPLAY_PROJECTION,
+ EPSG_PROJECTION),
+ {icon:iconclone});
+ feature.pk = mark.properties.pk;
+ feature.popupClass = settings.popupClass;
+ feature.data.popupContentHTML = "";
+ if (!settings.popupContentFull) {
+ feature.data.popupContentHTML += mark.properties.name;
+ }
+ feature.data.popupContentHTML += "
";
+ feature.data.overflow = 'hidden';
+ var marker = feature.createMarker();
+ marker.pk = feature.pk;
+ marker.icon_url = icon_url;
+ marker.icon_hover_url = icon_hover_url;
+ marker.category_name = mark.properties.category_name;
+ /* manage markers events */
+ var _popup = function() {
+ /* show the popup */
+ if (settings.current_popup != null) {
+ settings.current_popup.hide();
+ }
+ if (feature.popup == null) {
+ feature.popup = feature.createPopup();
+ settings.map.addPopup(feature.popup);
+ } else {
+ feature.popup.toggle();
+ }
+ settings.current_popup = feature.popup;
+ /* hide on click on the cloud */
+ if (!settings.explicit_popup_hide){
+ settings.current_popup.groupDiv.onclick = methods.hidePopup;
+ }
+ settings.permalink.updateLink();
+ }
+ var _repan_popup = function(){
+ /* re-pan manually */
+
+ // no clean way to detect if all the element are ready
+ // lack of better...
+ setTimeout(
+ function(){
+ settings.current_popup.panIntoView();
+ }, 1000);
+ }
+
+ var markerClick = function (evt) {
+ settings.current_feature = feature;
+ methods.setCurrentPosition(feature.lonlat);
+ if ( settings.on_marker_click ) {
+ settings.on_marker_click(evt, mark, settings);
+ }
+ else
+ {
+ methods.center_on_feature();
+ $('#chimere_map_menu').hide();
+ // Default popup
+ if (feature.popup && feature.popup.visible()) {
+ if (settings.current_popup == feature.popup) {
+ feature.popup.hide();
+ if (!settings.simple){
+ $('#detail').hide();
+ }
+ } else {
+ settings.current_popup.hide();
+ _popup();
+ methods.display_feature_detail(feature.pk);
+ _repan_popup();
+ }
+ } else {
+ _popup();
+ methods.display_feature_detail(feature.pk);
+ _repan_popup();
+ }
+ }
+ OpenLayers.Event.stop(evt);
+ };
+ var markerOver = function (evt) {
+ document.body.style.cursor='pointer';
+ if (settings.current_feature && settings.current_feature.popup
+ && settings.current_feature.popup.visible()) return;
+ var marker = evt.object;
+ if (marker.icon_hover_url){
+ marker.setUrl(marker.icon_hover_url);
+ }
+ px = settings.map.getPixelFromLonLat(marker.lonlat);
+ marker_hover = $('#'+settings.marker_hover_id);
+ marker_hover_content = $('#'+settings.marker_hover_content_id);
+ marker_hover_content.html(marker.category_name);
+ var map_position = $(settings.map.div).offset();
+
+ var width = marker_hover.width();
+ width += parseInt(marker_hover.css("padding-left"), 10)
+ + parseInt(marker_hover.css("padding-right"), 10)
+ + parseInt(marker_hover.css("margin-left"), 10)
+ + parseInt(marker_hover.css("margin-right"), 10)
+ + parseInt(marker_hover.css("borderLeftWidth"), 10)
+ + parseInt(marker_hover.css("borderRightWidth"), 10);
+ var pos_x = px.x + map_position.left
+ - width/2 + 1;
+ if (settings.marker_hover_offset)
+ pos_x += settings.marker_hover_offset.x;
+ $('#'+settings.marker_hover_id).css('left', pos_x);
+ var height = marker_hover.height();
+ height += parseInt(marker_hover.css("padding-top"), 10)
+ + parseInt(marker_hover.css("padding-bottom"), 10)
+ + parseInt(marker_hover.css("margin-top"), 10)
+ + parseInt(marker_hover.css("margin-bottom"), 10)
+ + parseInt(marker_hover.css("borderBottomWidth"), 10)
+ + parseInt(marker_hover.css("borderTopWidth"), 10);
+ var pos_y = px.y + map_position.top
+ - height - marker.icon.size.h;
+ if (settings.marker_hover_offset)
+ pos_y += settings.marker_hover_offset.y;
+ $('#'+settings.marker_hover_id).css('top', pos_y);
+ $('#'+settings.marker_hover_id).show();
+ OpenLayers.Event.stop(evt);
+ };
+ var markerOut = function (evt) {
+ document.body.style.cursor='auto';
+ var marker = evt.object;
+ if (marker.icon_hover_url){
+ marker.setUrl(marker.icon_url);
+ }
+ $('#'+settings.marker_hover_id).hide();
+ OpenLayers.Event.stop(evt);
+ };
+ marker.events.register('click', feature, markerClick);
+ marker.events.register('mouseover', feature, markerOver);
+ marker.events.register('mouseout', feature, markerOut);
+ settings.layerMarkers.addMarker(marker);
+ /* show the item when designed in the permalink */
+ if (settings.display_feature == feature.pk){
+ settings.current_feature = feature;
+ _popup(feature);
+ methods.display_feature_detail(feature.pk);
+ if (!settings.display_route){
+ settings.map.setCenter(feature.lonlat, 16);
+ _repan_popup();
+ }
+ settings.display_feature = null;
+ //methods.loadCategories();
+ }
+
+ if (settings.enable_clustering){
+ // manage cluster layer
+ var point = new OpenLayers.Geometry.Point(lon, lat).transform(
+ EPSG_DISPLAY_PROJECTION,
+ EPSG_PROJECTION);
+ var feat = new OpenLayers.Feature.Vector(point);
+ feat.attributes = { icon: MEDIA_URL + mark.properties.icon_path,
+ name: "", label:"", pk:mark.properties.pk,
+ marker:marker};
+ settings.cluster_array.push(feat);
+ }
+
+ return feature;
+ },
+
+ /*
+ * Put a route on the map
+ */
+ addRoute: function(route) {
+ var polyline = route.geometry;
+ var point_array = new Array();
+ for (i=0; i 0){
+ uri += '_';
+ }
+ uri += step.lon + '_' + step.lat;
+ }
+ settings.permalink.updateLink();
+ $.ajax({url: uri,
+ dataType: "json",
+ success: function (data) {
+ settings.layerRoute.removeAllFeatures();
+ methods.redrawRoutingIcons();
+ methods.hideMessage();
+ if (!data.features.length){
+ methods.displayMessage(routing_fail_message);
+ return;
+ }
+ for (var i = 0; i < data.features.length; i++) {
+ var feat = data.features[i];
+ if(feat.type == 'LineString'){
+ settings.current_route_feature =
+ methods.putRoute(feat);
+ } else {
+ var lonlat = new OpenLayers.LonLat(
+ feat.geometry.coordinates[0],
+ feat.geometry.coordinates[1]);
+ lonlat.transform(EPSG_DISPLAY_PROJECTION,
+ settings.map.getProjectionObject());
+ var icon_height = feat.properties.icon_height;
+ var icon_width = feat.properties.icon_width;
+ var marker = new OpenLayers.Marker(lonlat,
+ new OpenLayers.Icon(
+ feat.properties.icon_path,
+ new OpenLayers.Size(icon_width,
+ icon_height),
+ new OpenLayers.Pixel(
+ -(icon_width/2),
+ -icon_height))
+ );
+ settings.layerRouteMarker.addMarker(marker);
+ }
+ }
+ if (data.message) methods.displayMessage(data.message);
+ settings.map.zoomToExtent(
+ settings.layerRoute.getDataExtent());
+ settings.map.zoomOut();
+ $('#id_transport_it').find('span'
+ ).removeClass('selected');
+ $('#id_transport_it_'+data.properties.transport
+ ).addClass('selected');
+ $('#chimere_total_label').html(
+ data.properties.total);
+ $('#chimere_itinerary_content').html(
+ data.properties.description);
+ $('#chimere_itinerary').show();
+ if(!settings.edition_type_is_route){
+ $('#chimere_itinerary_form').hide();
+ settings.routing_panel_open();
+ } else {
+ methods.updateRoutingInput();
+ }
+ },
+ error: function (jqXHR, textStatus, errorThrown) {
+ methods.redrawRoutingIcons();
+ methods.hideMessage();
+ console.log(errorThrown);
+ console.log(textStatus);
+ settings.layerRoute.removeAllFeatures();
+ methods.displayMessage(routing_fail_message);
+ }
+ });
+
+ },
+ /*
+ Put a route on the map
+ */
+ putRoute: function(polyline) {
+ var point_array = new Array();
+ for (i=0; i" + data + "");
+ }
+ }
+ }
+ });
+ },
+ displayMessage: function(message){
+ if (!$('#chimere_message').length) return;
+ $('#chimere_message').html(message);
+ $('#chimere_message').dialog('open');
+ },
+ hideMessage: function(message){
+ if (!$('#chimere_message').length) return;
+ $('#chimere_message').dialog('close');
+ },
+ center_on_feature: function(feature) {
+ var f = get_or_set(feature, settings.current_feature);
+ if (f)
+ {
+ settings.map.setCenter(f.lonlat);
+ }
+ },
+ zoom: function (options) {
+ if ($.hasattr("category", options)) {
+ helpers.zoom_to_category(options["category"]);
+ } else if ($.hasattr("subcategories", options)) {
+ helpers.zoom_to_subcategories(options["subcategories"]);
+ } else if ($.hasattr("area", options)) {
+ helpers.zoom_to_area(options["area"]);
+ }
+ },
+ category_detail: function (category_id) {
+ /* show the detail of a category */
+ var uri = extra_url + "getDescriptionDetail/" + category_id;
+ $.ajax({url:uri,
+ success: function (data) {
+ $("#category_description").html(data).dialog();
+ $("#category_description").dialog( "option", "title",
+ $("#category_title").html());
+ }
+ });
+ },
+ /*
+ * Load the subcategory description if available
+ */
+ subcategory_detail: function(category_id){
+ var uri = extra_url + "getCategory/" + category_id;
+
+ $.ajax({url: uri,
+ dataType: "json",
+ success: function (data) {
+ if (!data.description){return}
+ $('#category_description').html(data.description);
+ $("#category_description").dialog("option", "title",
+ data.name);
+ $('#category_description').dialog('open');
+ },
+ error: function (data) {
+ // fail silently
+ }
+ });
+ },
+ toggle_category: function (id) {
+ // TODO make this id DOM element customisable
+ // Check if element is currently visible or not
+ var was_visible = $("#maincategory_" + id).is(":visible");
+ // Close all categories
+ var category_plus = STATIC_URL + "chimere/img/plus.png";
+ var category_minus = STATIC_URL + "chimere/img/minus.png";
+ if (settings.category_accordion){
+ $("#categories ul.subcategories").hide();
+ $("#categories img.toggle_category").attr("src", category_plus);
+ $("#categories .main_category").addClass("toggle_plus");
+ $("#categories .main_category").removeClass("toggle_minus");
+ }
+ // Put a minus image
+ if (!was_visible)
+ {
+ // Show the subcategories
+ $("#maincategory_" + id).toggle();
+ $("#maincategory_" + id).parent().addClass("toggle_minus");
+ $("#maincategory_" + id).parent().removeClass("toggle_plus");
+ // Put a plus image
+ $("#maincategory_img_" + id).attr("src", category_minus);
+ settings.current_category = id;
+ }
+ if (!settings.category_accordion && was_visible)
+ {
+ $("#maincategory_" + id).toggle();
+ $("#maincategory_" + id).parent().addClass("toggle_plus");
+ $("#maincategory_" + id).parent().removeClass("toggle_minus");
+ // Put a minus image
+ $("#maincategory_img_" + id).attr("src", category_plus);
+ }
+ },
+ zoomToCurrentExtent: function(){
+ /* zoom to current extent */
+ var current_extent = helpers.getSavedExtent();
+ var extent;
+ if (OpenLayers && current_extent && current_extent.length == 4){
+ extent = new OpenLayers.Bounds(
+ current_extent[0], current_extent[1],
+ current_extent[2], current_extent[3]);
+ }
+ /*
+ else if (OpenLayers && default_area && default_area.length == 4){
+ extent = new OpenLayers.Bounds(default_area[0], default_area[1],
+ default_area[2], default_area[3]);
+ }*/
+ else{
+ return;
+ }
+ extent.transform(EPSG_DISPLAY_PROJECTION, EPSG_PROJECTION);
+ settings.map.zoomToExtent(extent, true);
+ return true;
+ },
+ // methods for edition
+ setMarker: function (event){
+ event = event || window.event;
+ var lonlat = settings.map.getLonLatFromViewPortPx(event.xy);
+ methods.putEditMarker(lonlat, false);
+ OpenLayers.Event.stop(event);
+ },
+ /* put the marker on the map and update latitude and longitude fields */
+ putEditMarker: function (lonlat, zoom){
+ if (settings.current_edit_feature) {
+ settings.layerMarkers.removeMarker(settings.current_edit_feature);
+ }
+ settings.current_edit_feature = new OpenLayers.Marker(lonlat.clone(),
+ settings.default_icon);
+ settings.layerMarkers.addMarker(settings.current_edit_feature);
+ methods.updateMarkerInput();
+ /* zoom to the point */
+ if (zoom){
+ var bounds = settings.layerMarkers.getDataExtent();
+ if (bounds) settings.map.zoomToExtent(bounds);
+ }
+ return;
+ },
+ updateMarkerInput: function(){
+ if (!settings.current_edit_feature) {
+ return;
+ }
+ lonlat = settings.current_edit_feature.lonlat.clone().transform(
+ settings.map.getProjectionObject(),
+ EPSG_DISPLAY_PROJECTION);
+ $('#id_point').val('POINT(' + lonlat.lon + ' ' + lonlat.lat + ')');
+ if($('#live_latitude').length){
+ $('#live_latitude').val(lonlat.lat);
+ $('#live_longitude').val(lonlat.lon);
+ }
+
+ },
+ updateRoutingInput: function(){
+ if (!settings.current_route_feature) {
+ return;
+ }
+ var current_geo = settings.current_route_feature.geometry.clone();
+ current_geo.transform(EPSG_PROJECTION, EPSG_DISPLAY_PROJECTION);
+ jQuery('#id_route').val(current_geo);
+ },
+ activateCurrentControl: function(){
+ if (settings.current_control){
+ settings.current_control.activate();
+ } else {
+ var pathCreate = settings.map.getControlsByClass(
+ 'OpenLayers.Control.DrawFeature');
+ if (pathCreate){
+ pathCreate[0].activate();
+ }
+ }
+ var pathModify = settings.map.getControlsByClass(
+ 'OpenLayers.Control.ModifyFeature');
+ if (settings.current_feature && pathModify){
+ pathModify[0].selectControl.select(settings.current_feature);
+ }
+ },
+ deactivateCurrentControl: function(){
+ if (settings.current_control){
+ settings.current_control.deactivate();
+ }
+ },
+ initFeature: function(json_geometry){
+ var json = new OpenLayers.Format.JSON();
+ var polyline = json.read(json_geometry);
+ var point_array = new Array();
+ for (i=0; i
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see .
-
-See the file COPYING for details.
-*/
-
-/* Add OpenLayers MapQuest layer management */
-OpenLayers.Layer.MapQuestOSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {
- name: "MapQuestOSM",
- sphericalMercator: true,
- url: ' http://otile1.mqcdn.com/tiles/1.0.0/osm/${z}/${x}/${y}.png',
- clone: function(obj) {
- if (obj == null) {
- obj = new OpenLayers.Layer.OSM(
- this.name, this.url, this.getOptions());
- }
- obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);
- return obj;
- },
- CLASS_NAME: "OpenLayers.Layer.MapQuestOSM"
-});
-
-/*
-* Little hasattr helper
-*/
-(function ($) {
- $.hasattr = function (key, arr) {
- var v = arr[key];
- if (typeof v === "undefined")
- return false;
- else
- return v; };
-})( jQuery );
-
-(function ($) {
- /*
- * Chimere jQuery plugin
- */
- /*
- * Default settings
- */
- var defaults = {
- restricted_extent: false,
- permalink_label: null,
- permalink_div: null,
- permalink: null, // OL Control, could be overrided
- map_layers: null,
- selected_map_layer: null,
- dynamic_categories: false,
- display_submited: false,
- display_feature: null,
- display_route: null,
- area_id: null,
- checked_categories: [],
- zoom: null,
- lat: null,
- lon: null,
- simple: false,
- routing_start_lat: null,
- routing_start_lon: null,
- routing_end_lat: null,
- routing_end_lon: null,
- routing_steps_lonlat: null,
- // Provide this function to make a custom click event on the marker
- on_marker_click: null,
- // Provide this function to override the feature detail display
- display_feature_detail_fx: null,
- // Provide this function for overriding the getSubcategories default
- get_subcategories_fx: null,
- hide_popup_fx: null,
- // if leave to false every click on the map hide the pop-up
- explicit_popup_hide: false,
- controls:[new OpenLayers.Control.Navigation(),
- new OpenLayers.Control.PanPanel(),
- new OpenLayers.Control.ZoomPanel(),
- new OpenLayers.Control.ScaleLine()],
- popupClass: OpenLayers.Popup.FramedCloud,
- popupContentFull: false, // if true the detail is inside the popup
- category_accordion: true, // category opening behave like an accordion
- maxResolution: 156543.0399,
- units: 'm',
- projection: new OpenLayers.Projection('EPSG:4326'),
- theme: null,
- enable_clustering: false,
- routing: false, // enable routing management
- routing_panel_open: function(){
- $('#chimere_itinerary_panel').dialog('open');
- },
- current_feature: null, // To store the active POI
- current_control: null, // To store the current control
- current_popup: null, // To store the current POI popup displayed
- current_category: null, // To store the current category clicked in list
- current_route_feature: null, // To store the current route find by routing
- itinerary_step_number:0, // current step number
- icon_offset: new OpenLayers.Pixel(0, 0),
- edition: false, // edition mode
- edition_type_is_route: false, // route or POI edition
- default_icon: new OpenLayers.Icon(
- 'http://www.openlayers.org/dev/img/marker-green.png',
- new OpenLayers.Size(21, 25),
- new OpenLayers.Pixel(-(21/2), -25)),
- cluster_icon: null,
- marker_hover_id:'marker_hover',
- marker_hover_content_id:'marker_hover_content',
- marker_hover_offset: null,
- icon_start: null,
- icon_step: null,
- icon_end: null
- };
- var settings = {};
- /*
- * Publics methods
- */
- var methods = {
- /*
- * Plugin init function
- */
- init: function ( options ) {
- /* Manage parameters */
- // not staticaly in default because of STATIC_URL init
- if (defaults.cluster_icon == null && typeof STATIC_URL != 'undefined'){
- defaults.cluster_icon = new OpenLayers.Icon(
- STATIC_URL+'chimere/img/marker-cluster.png',
- new OpenLayers.Size(36, 39),
- new OpenLayers.Pixel(-(36/2), -(39/2)));
- }
- if (defaults.icon_start == null && typeof STATIC_URL != 'undefined'){
- defaults.icon_start = new OpenLayers.Icon(
- STATIC_URL + "chimere/img/flag-start.png",
- new OpenLayers.Size(32, 32),
- new OpenLayers.Pixel(0, -32));
- }
- if (defaults.icon_step == null && typeof STATIC_URL != 'undefined'){
- defaults.icon_step = new OpenLayers.Icon(
- STATIC_URL + "chimere/img/flag-step.png",
- new OpenLayers.Size(32, 32),
- new OpenLayers.Pixel(0, -32));
- }
- if (defaults.icon_end == null && typeof STATIC_URL != 'undefined'){
- defaults.icon_end = new OpenLayers.Icon(
- STATIC_URL + "chimere/img/flag-finish.png",
- new OpenLayers.Size(32, 32),
- new OpenLayers.Pixel(0, -32));
- }
- settings = $.extend({}, defaults);
- if ( options ) $.extend(settings, options);
- var map_element = $(this).get(0);
- var map_options = {
- controls: settings.controls,
- maxResolution: settings.maxResolution,
- units: settings.units,
- projection: settings.projection,
- theme: settings.theme
- };
- if (settings.restricted_extent){
- settings.restricted_extent.transform(EPSG_DISPLAY_PROJECTION,
- EPSG_PROJECTION);
- map_options['restrictedExtent'] = settings.restricted_extent;
- }
-
- settings.current_position = null;
-
- /* Create map object */
- settings.map = map = new OpenLayers.Map(map_element, map_options);
-
- map.addControl(new OpenLayers.Control.Attribution());
-
- /* Manage permalink */
- if (!settings.edition){
- if (settings.permalink == null && !settings.edition) {
- var permalink_options = {};
- if (settings.permalink_element){
- // hard to understand from OL documentation...
- permalink_options["div"] = settings.permalink_element;
- }
- settings.permalink = new OpenLayers.Control.Permalink(
- permalink_options);
- }
- /* HACK new permalink createParams method
- FIXME when facilities are given to personalize the permalink */
- var oldCreateParams = settings.permalink.createParams
- var _createParams = function(center, zoom, layers) {
- // Call normal method
- var params = oldCreateParams(center, zoom, layers);
- // Make specific params
- params.checked_categories = settings.checked_categories;
- params.display_submited = settings.display_submited;
- if(settings.current_feature)
- params.current_feature = settings.current_feature.pk;
- if (settings.routing_start){
- lonlat = settings.routing_start.lonlat.clone().transform(
- EPSG_PROJECTION, EPSG_DISPLAY_PROJECTION);
- params.routing_start_lon = lonlat.lon;
- params.routing_start_lat = lonlat.lat;
- }
- if (settings.routing_end){
- lonlat = settings.routing_end.lonlat.clone().transform(
- EPSG_PROJECTION, EPSG_DISPLAY_PROJECTION);
- params.routing_end_lon = lonlat.lon;
- params.routing_end_lat = lonlat.lat;
- }
- if (settings.routing_steps){
- var steps = [];
- for (var i = 0; i < settings.routing_steps.length; i++){
- lonlat = settings.routing_steps[i].lonlat.clone(
- ).transform(EPSG_PROJECTION,
- EPSG_DISPLAY_PROJECTION);
- steps.push([lonlat.lon, lonlat.lat]);
- }
- params.routing_steps = steps;
- }
- return params;
- }
- // Force new createParams method
- settings.permalink.createParams = _createParams;
- settings.map.addControl(settings.permalink);
- // update with the translated permalink label
- if(settings.permalink_label && settings.permalink.div
- && settings.permalink.div.childNodes.length > 0){
- settings.permalink.div.childNodes[0].textContent = settings.permalink_label;
- }
- }
- /* Add Layers */
- settings.map.addLayers(settings.map_layers);
- if (settings.map_layers.length > 1){
- settings.map.addControl(new OpenLayers.Control.LayerSwitcher(
- {roundedCorner:false}));
- }
- /* select the default map layer */
- if (!settings.selected_map_layer){
- settings.selected_map_layer = 0;
- }
- settings.map.setBaseLayer(
- settings.map_layers[settings.selected_map_layer]);
-
- /* manage the context menu */
- $('#map_menu_zoomin').bind("click", methods.zoomIn);
- $('#map_menu_zoomout').bind("click", methods.zoomOut);
- $('#map_menu_center').bind("click", methods.mapCenter);
- /* manage the routing */
- if (settings.routing){
- settings.routing_start = null;
- settings.routing_steps = new Array();
- settings.routing_end = null;
- $('#map_menu_from').bind("click", methods.routingFrom);
- $('#map_menu_step').bind("click", methods.routingAddStep);
- $('#map_menu_to').bind("click", methods.routingTo);
- $('#map_menu_clear').bind("click", methods.routingClear);
- settings.layerRoute = new OpenLayers.Layer.Vector("Route Layer");
- settings.map.addLayer(settings.layerRoute);
- settings.layerRouteMarker = new OpenLayers.Layer.Markers(
- 'Route markers');
- settings.map.addLayer(settings.layerRouteMarker);
- }
- /* Vectors layer */
- settings.layerVectors = new OpenLayers.Layer.Vector("Vector Layer");
- settings.map.addLayer(settings.layerVectors);
- settings.layerVectors.setOpacity(0.8);
- if (settings.edition_type_is_route){
- settings.map.addControl(new OpenLayers.Control.DrawFeature(
- settings.layerVectors, OpenLayers.Handler.Path));
- settings.map.addControl(new OpenLayers.Control.ModifyFeature(
- settings.layerVectors, {clickout:false, toggle:false}));
- }
-
- if (settings.enable_clustering){
- var style = new OpenLayers.Style({
- graphicTitle: "${name}",
- externalGraphic: "${icon}",
- graphicWidth: "${width}",
- graphicHeight: "${height}",
- graphicXOffset: "${offsetx}",
- graphicYOffset: "${offsety}",
- graphicOpacity: 1,
- label: "${label}",
- labelYOffset: "2",
- fontSize:'1.3em'
- }, {
- context: {
- name: function(feature) {
- if(feature.cluster) {
- feature.attributes.width = settings.cluster_icon.size.w;
- feature.attributes.height = settings.cluster_icon.size.h;
- feature.attributes.offsetx = settings.cluster_icon.offset.x;
- feature.attributes.offsety = settings.cluster_icon.offset.y;
- } else{
- var marker = feature.attributes.marker
- feature.attributes.width = marker.icon.size.w;
- feature.attributes.height = marker.icon.size.h;
- feature.attributes.offsetx = settings.icon_offset.x;
- feature.attributes.offsety = settings.icon_offset.y;
- }
- return feature.attributes.name;
- },
- label: function(feature) {
- // clustered features count or blank if feature is not a cluster
- return feature.cluster ? feature.cluster.length : "";
- },
- icon: function(feature) {
- if (feature.cluster){
- return settings.cluster_icon.url;
- } else {
- return STATIC_URL + 'chimere/img/empty.png';
- }
- },
- width: function(feature) { return feature.attributes.width; },
- height: function(feature) { return feature.attributes.height; },
- offsetx: function(feature) { return feature.attributes.offsetx; },
- offsety: function(feature) { return feature.attributes.offsety; }
- }});
-
-
- /* Cluster layer */
- settings.clustering = new OpenLayers.Strategy.Cluster({
- distance: 10,
- threshold: 2});
- settings.layerCluster = new OpenLayers.Layer.Vector("Cluster layer",
- {styleMap: new OpenLayers.StyleMap({'default': style}),
- strategies: [settings.clustering]});
- settings.map.addLayer(settings.layerCluster);
-
- var highlightCtrl = new OpenLayers.Control.SelectFeature(
- settings.layerCluster, {
- hover: true,
- highlightOnly: true,
- eventListeners: {
- featurehighlighted: function(e) {
- if(e.feature.attributes.marker)
- e.feature.attributes.marker.events.triggerEvent('mouseover');
- },
- featureunhighlighted: function(e) {
- if(e.feature.attributes.marker)
- e.feature.attributes.marker.events.triggerEvent('mouseout');
- }
- }
- });
-
- var selectCtrl = new OpenLayers.Control.SelectFeature(
- settings.layerCluster,{
- onSelect: methods.zoomOnCluster
- });
-
- settings.map.addControl(highlightCtrl);
- settings.map.addControl(selectCtrl);
-
- highlightCtrl.activate();
- selectCtrl.activate();
- }
-
- /* Markers layer */
- settings.layerMarkers = new OpenLayers.Layer.Markers('POIs');
- settings.map.addLayer(settings.layerMarkers);
-
- if (settings.dynamic_categories){
- settings.map.events.register('moveend', settings.map,
- methods.loadCategories);
- }
- /* TODO make a function */
- if (settings.display_submited) {
- document.getElementById('display_submited_check').checked = true;
- }
- /* if we have some zoom and lon/lat from the init options */
- if (settings.zoom && settings.lon && settings.lat) {
- var centerLonLat = new OpenLayers.LonLat(settings.lon,
- settings.lat);
- settings.map.setCenter(centerLonLat, settings.zoom);
- }
- /* if not zoom to the extent in cookies */
- else if (!methods.zoomToCurrentExtent(settings.map)){
- /* if no extent in cookies zoom to default */
- if(CENTER_LONLAT && DEFAULT_ZOOM){
- settings.map.setCenter(CENTER_LONLAT, DEFAULT_ZOOM);
- }
- }
-
- if (!settings.edition){
- if (settings.enable_clustering){
- settings.map.events.register('zoomend', null,
- methods.cleanCluster);
- }
- methods.loadCategories();
- methods.loadGeoObjects();
- methods.activateContextMenu()
- } else {
- if (!settings.edition_type_is_route){
- methods.activateMarkerEdit();
- } else {
- settings.layerVectors.events.register('featuremodified',
- settings.layerVectors, helpers.updateRouteForm);
- settings.layerVectors.events.register('featureadded',
- settings.layerVectors, helpers.featureRouteCreated);
- }
- }
- if (settings.routing_start_lon && settings.routing_start_lat){
- settings.current_position = new OpenLayers.LonLat(
- settings.routing_start_lon, settings.routing_start_lat
- ).transform(EPSG_DISPLAY_PROJECTION, EPSG_PROJECTION);
- methods.routingFrom();
- }
- if (settings.routing_end_lon && settings.routing_end_lat){
- settings.current_position = new OpenLayers.LonLat(
- settings.routing_end_lon, settings.routing_end_lat
- ).transform(EPSG_DISPLAY_PROJECTION, EPSG_PROJECTION);
- methods.routingTo();
- }
- if (settings.routing_steps_lonlat){
- for (var i = 0; i < settings.routing_steps_lonlat.length/2; i++) {
- lon = settings.routing_steps_lonlat[i*2];
- lat = settings.routing_steps_lonlat[i*2+1];
- settings.current_position = new OpenLayers.LonLat(lon, lat
- ).transform(EPSG_DISPLAY_PROJECTION, EPSG_PROJECTION);
- methods.routingAddStep();
- }
- }
- methods.preload_images();
- }, // end of init
- /* Preload icons */
- preload_images: function(){
- if (typeof extra_url == 'undefined') return;
- var uri = extra_url + "getAllCategories/";
- $.ajax({url: uri,
- dataType: "json",
- success: function (data) {
- if (!data.categories){return}
- for(var idx=0; idx span");
- if (parent.find('input[type=checkbox]:checked').length){
- parent_label.addClass('category-selected');
- } else {
- parent_label.removeClass('category-selected');
- }
- var master_check = parent.find("> input");
- if (parent.find('.subcategories input[type=checkbox]').length ==
- parent.find('.subcategories input[type=checkbox]:checked').length){
- master_check.attr('checked', 'checked');
- } else {
- master_check.removeAttr('checked');
- }
-
- if($('#action-categories').length){
- if ($('#categories input[type=checkbox]:checked').length){
- $('#action-categories').addClass('category-selected');
- } else {
- $('#action-categories').removeClass('category-selected');
- }
- }
- return master_check;
- };
- var _init_categories = function () {
- /*
- * Add event listener in categories DOM elements
- */
- $('#categories #ul_categories > li > input').bind("click",
- function (e) {
- methods.hidePopup(e);
- _toggle_subcategories($(this));
- methods.loadGeoObjects();
- settings.permalink.updateLink();
- });
- $('.subcategories li input').bind("click", function (e) {
- var c_name = $(this).attr('name');
- c_name = c_name.substr(c_name.lastIndexOf("_")+1);
- if($(this).is(':checked')){
- methods.subcategory_detail(c_name);
- }
- var par = $(this).parent();
- if ($(this).attr('checked')){
- par.addClass('selected');
- } else {
- par.removeClass('selected');
- }
- methods.hidePopup(e);
- methods.loadGeoObjects();
- _toggle_categories($(this));
- settings.permalink.updateLink();
- if ($('#layer_cat_'+c_name).length){
- $('#layer_cat_'+c_name).prop("checked",
- this.checked);
- }
- });
- $('#display_submited_check').bind("click", function () {
- methods.loadGeoObjects();
- settings.permalink.updateLink();
- });
- // Zoom to category
- $(".zoom_to_category").bind("click", function (e) {
- var id = this.id.substr(this.id.lastIndexOf("_")+1);
- helpers.zoom_to_category(id);
- });
- $(".zoom_to_subcategory").bind("click", function (e) {
- var id = this.id.substr(this.id.lastIndexOf("_")+1);
- helpers.zoom_to_subcategories([id]);
- });
- $(".toggle_category").parent().bind("click", function (e) {
- var item = $(this).children('.toggle_category');
- var id = item.attr('id').substr(item.attr('id').lastIndexOf("_")+1);
- methods.toggle_category(id);
- });
- }
- var _reCheckCategories = function (){
- /* recheck categories on init or when a redraw occurs */
- if (!settings.checked_categories){
- return;
- }
- $('#frm_categories .subcategories input:checkbox').each(function(index){
- cat_id = $(this).attr('id').split('_').pop();
- if (settings.checked_categories.indexOf(parseInt(cat_id)) != -1) {
- $(this).attr("checked", "checked");
- _toggle_categories($(this));
- methods.toggle_category();
- } else {
- $(this).attr("checked", false);
- }
- });
- if (settings.display_submited == true){
- $('#display_submited_check').attr("checked", "checked");
- }
- }
- },
- /*
- * Hide clusterized markers
- */
- cleanCluster: function (){
- if (settings.map.getZoom() === 18) {
- // Don't cluster at this level. No matter what.
- settings.clustering.threshold = 1000;
- } else {
- settings.clustering.threshold = 2;
- }
- //settings.layerCluster.refresh({force:true});
- settings.clustering.recluster();
- var hidden_feature_idx = [];
- if (settings.map.getZoom() != 18) {
- for(var idx=0; idx -1){
- settings.layerMarkers.markers[j].display(false);
- } else {
- settings.layerMarkers.markers[j].display(true);
- }
- }
- },
- /*
- * Put a marker on the map
- */
- addMarker: function (mark) {
- /*
- * Default Feature configuration
- * This can be overrided in on_marker_click, using settings.current_feature
- */
- var lat = mark.geometry.coordinates[1];
- var lon = mark.geometry.coordinates[0];
- var size = new OpenLayers.Size(mark.properties.icon_width,
- mark.properties.icon_height);
- var icon_url = MEDIA_URL + mark.properties.icon_path;
- var icon_hover_url = '';
- if (mark.properties.icon_hover_path){
- var icon_hover_url = MEDIA_URL + mark.properties.icon_hover_path;
- }
- var iconclone = new OpenLayers.Icon(icon_url, size,
- settings.icon_offset);
-
- var feature = new OpenLayers.Feature(settings.layerMarkers,
- new OpenLayers.LonLat(lon, lat).transform(
- EPSG_DISPLAY_PROJECTION,
- EPSG_PROJECTION),
- {icon:iconclone});
- feature.pk = mark.properties.pk;
- feature.popupClass = settings.popupClass;
- feature.data.popupContentHTML = "";
- if (!settings.popupContentFull) {
- feature.data.popupContentHTML += mark.properties.name;
- }
- feature.data.popupContentHTML += "
";
- feature.data.overflow = 'hidden';
- var marker = feature.createMarker();
- marker.pk = feature.pk;
- marker.icon_url = icon_url;
- marker.icon_hover_url = icon_hover_url;
- marker.category_name = mark.properties.category_name;
- /* manage markers events */
- var _popup = function() {
- /* show the popup */
- if (settings.current_popup != null) {
- settings.current_popup.hide();
- }
- if (feature.popup == null) {
- feature.popup = feature.createPopup();
- settings.map.addPopup(feature.popup);
- } else {
- feature.popup.toggle();
- }
- settings.current_popup = feature.popup;
- /* hide on click on the cloud */
- if (!settings.explicit_popup_hide){
- settings.current_popup.groupDiv.onclick = methods.hidePopup;
- }
- settings.permalink.updateLink();
- }
- var _repan_popup = function(){
- /* re-pan manually */
-
- // no clean way to detect if all the element are ready
- // lack of better...
- setTimeout(
- function(){
- settings.current_popup.panIntoView();
- }, 1000);
- }
-
- var markerClick = function (evt) {
- settings.current_feature = feature;
- methods.setCurrentPosition(feature.lonlat);
- if ( settings.on_marker_click ) {
- settings.on_marker_click(evt, mark, settings);
- }
- else
- {
- methods.center_on_feature();
- $('#chimere_map_menu').hide();
- // Default popup
- if (feature.popup && feature.popup.visible()) {
- if (settings.current_popup == feature.popup) {
- feature.popup.hide();
- if (!settings.simple){
- $('#detail').hide();
- }
- } else {
- settings.current_popup.hide();
- _popup();
- methods.display_feature_detail(feature.pk);
- _repan_popup();
- }
- } else {
- _popup();
- methods.display_feature_detail(feature.pk);
- _repan_popup();
- }
- }
- OpenLayers.Event.stop(evt);
- };
- var markerOver = function (evt) {
- document.body.style.cursor='pointer';
- if (settings.current_feature && settings.current_feature.popup
- && settings.current_feature.popup.visible()) return;
- var marker = evt.object;
- if (marker.icon_hover_url){
- marker.setUrl(marker.icon_hover_url);
- }
- px = settings.map.getPixelFromLonLat(marker.lonlat);
- marker_hover = $('#'+settings.marker_hover_id);
- marker_hover_content = $('#'+settings.marker_hover_content_id);
- marker_hover_content.html(marker.category_name);
- var map_position = $(settings.map.div).offset();
-
- var width = marker_hover.width();
- width += parseInt(marker_hover.css("padding-left"), 10)
- + parseInt(marker_hover.css("padding-right"), 10)
- + parseInt(marker_hover.css("margin-left"), 10)
- + parseInt(marker_hover.css("margin-right"), 10)
- + parseInt(marker_hover.css("borderLeftWidth"), 10)
- + parseInt(marker_hover.css("borderRightWidth"), 10);
- var pos_x = px.x + map_position.left
- - width/2 + 1;
- if (settings.marker_hover_offset)
- pos_x += settings.marker_hover_offset.x;
- $('#'+settings.marker_hover_id).css('left', pos_x);
- var height = marker_hover.height();
- height += parseInt(marker_hover.css("padding-top"), 10)
- + parseInt(marker_hover.css("padding-bottom"), 10)
- + parseInt(marker_hover.css("margin-top"), 10)
- + parseInt(marker_hover.css("margin-bottom"), 10)
- + parseInt(marker_hover.css("borderBottomWidth"), 10)
- + parseInt(marker_hover.css("borderTopWidth"), 10);
- var pos_y = px.y + map_position.top
- - height - marker.icon.size.h;
- if (settings.marker_hover_offset)
- pos_y += settings.marker_hover_offset.y;
- $('#'+settings.marker_hover_id).css('top', pos_y);
- $('#'+settings.marker_hover_id).show();
- OpenLayers.Event.stop(evt);
- };
- var markerOut = function (evt) {
- document.body.style.cursor='auto';
- var marker = evt.object;
- if (marker.icon_hover_url){
- marker.setUrl(marker.icon_url);
- }
- $('#'+settings.marker_hover_id).hide();
- OpenLayers.Event.stop(evt);
- };
- marker.events.register('click', feature, markerClick);
- marker.events.register('mouseover', feature, markerOver);
- marker.events.register('mouseout', feature, markerOut);
- settings.layerMarkers.addMarker(marker);
- /* show the item when designed in the permalink */
- if (settings.display_feature == feature.pk){
- settings.current_feature = feature;
- _popup(feature);
- methods.display_feature_detail(feature.pk);
- if (!settings.display_route){
- settings.map.setCenter(feature.lonlat, 16);
- _repan_popup();
- }
- settings.display_feature = null;
- //methods.loadCategories();
- }
-
- if (settings.enable_clustering){
- // manage cluster layer
- var point = new OpenLayers.Geometry.Point(lon, lat).transform(
- EPSG_DISPLAY_PROJECTION,
- EPSG_PROJECTION);
- var feat = new OpenLayers.Feature.Vector(point);
- feat.attributes = { icon: MEDIA_URL + mark.properties.icon_path,
- name: "", label:"", pk:mark.properties.pk,
- marker:marker};
- settings.cluster_array.push(feat);
- }
-
- return feature;
- },
-
- /*
- * Put a route on the map
- */
- addRoute: function(route) {
- var polyline = route.geometry;
- var point_array = new Array();
- for (i=0; i 0){
- uri += '_';
- }
- uri += step.lon + '_' + step.lat;
- }
- settings.permalink.updateLink();
- $.ajax({url: uri,
- dataType: "json",
- success: function (data) {
- settings.layerRoute.removeAllFeatures();
- methods.redrawRoutingIcons();
- methods.hideMessage();
- if (!data.features.length){
- methods.displayMessage(routing_fail_message);
- return;
- }
- for (var i = 0; i < data.features.length; i++) {
- var feat = data.features[i];
- if(feat.type == 'LineString'){
- settings.current_route_feature =
- methods.putRoute(feat);
- } else {
- var lonlat = new OpenLayers.LonLat(
- feat.geometry.coordinates[0],
- feat.geometry.coordinates[1]);
- lonlat.transform(EPSG_DISPLAY_PROJECTION,
- settings.map.getProjectionObject());
- var icon_height = feat.properties.icon_height;
- var icon_width = feat.properties.icon_width;
- var marker = new OpenLayers.Marker(lonlat,
- new OpenLayers.Icon(
- feat.properties.icon_path,
- new OpenLayers.Size(icon_width,
- icon_height),
- new OpenLayers.Pixel(
- -(icon_width/2),
- -icon_height))
- );
- settings.layerRouteMarker.addMarker(marker);
- }
- }
- if (data.message) methods.displayMessage(data.message);
- settings.map.zoomToExtent(
- settings.layerRoute.getDataExtent());
- settings.map.zoomOut();
- $('#id_transport_it').find('span'
- ).removeClass('selected');
- $('#id_transport_it_'+data.properties.transport
- ).addClass('selected');
- $('#chimere_total_label').html(
- data.properties.total);
- $('#chimere_itinerary_content').html(
- data.properties.description);
- $('#chimere_itinerary').show();
- if(!settings.edition_type_is_route){
- $('#chimere_itinerary_form').hide();
- settings.routing_panel_open();
- } else {
- methods.updateRoutingInput();
- }
- },
- error: function (jqXHR, textStatus, errorThrown) {
- methods.redrawRoutingIcons();
- methods.hideMessage();
- console.log(errorThrown);
- console.log(textStatus);
- settings.layerRoute.removeAllFeatures();
- methods.displayMessage(routing_fail_message);
- }
- });
-
- },
- /*
- Put a route on the map
- */
- putRoute: function(polyline) {
- var point_array = new Array();
- for (i=0; i" + data + "");
- }
- }
- }
- });
- },
- displayMessage: function(message){
- if (!$('#chimere_message').length) return;
- $('#chimere_message').html(message);
- $('#chimere_message').dialog('open');
- },
- hideMessage: function(message){
- if (!$('#chimere_message').length) return;
- $('#chimere_message').dialog('close');
- },
- center_on_feature: function(feature) {
- var f = get_or_set(feature, settings.current_feature);
- if (f)
- {
- settings.map.setCenter(f.lonlat);
- }
- },
- zoom: function (options) {
- if ($.hasattr("category", options)) {
- helpers.zoom_to_category(options["category"]);
- } else if ($.hasattr("subcategories", options)) {
- helpers.zoom_to_subcategories(options["subcategories"]);
- } else if ($.hasattr("area", options)) {
- helpers.zoom_to_area(options["area"]);
- }
- },
- category_detail: function (category_id) {
- /* show the detail of a category */
- var uri = extra_url + "getDescriptionDetail/" + category_id;
- $.ajax({url:uri,
- success: function (data) {
- $("#category_description").html(data).dialog();
- $("#category_description").dialog( "option", "title",
- $("#category_title").html());
- }
- });
- },
- /*
- * Load the subcategory description if available
- */
- subcategory_detail: function(category_id){
- var uri = extra_url + "getCategory/" + category_id;
-
- $.ajax({url: uri,
- dataType: "json",
- success: function (data) {
- if (!data.description){return}
- $('#category_description').html(data.description);
- $("#category_description").dialog("option", "title",
- data.name);
- $('#category_description').dialog('open');
- },
- error: function (data) {
- // fail silently
- }
- });
- },
- toggle_category: function (id) {
- // TODO make this id DOM element customisable
- // Check if element is currently visible or not
- var was_visible = $("#maincategory_" + id).is(":visible");
- // Close all categories
- var category_plus = STATIC_URL + "chimere/img/plus.png";
- var category_minus = STATIC_URL + "chimere/img/minus.png";
- if (settings.category_accordion){
- $("#categories ul.subcategories").hide();
- $("#categories img.toggle_category").attr("src", category_plus);
- $("#categories .main_category").addClass("toggle_plus");
- $("#categories .main_category").removeClass("toggle_minus");
- }
- // Put a minus image
- if (!was_visible)
- {
- // Show the subcategories
- $("#maincategory_" + id).toggle();
- $("#maincategory_" + id).parent().addClass("toggle_minus");
- $("#maincategory_" + id).parent().removeClass("toggle_plus");
- // Put a plus image
- $("#maincategory_img_" + id).attr("src", category_minus);
- settings.current_category = id;
- }
- if (!settings.category_accordion && was_visible)
- {
- $("#maincategory_" + id).toggle();
- $("#maincategory_" + id).parent().addClass("toggle_plus");
- $("#maincategory_" + id).parent().removeClass("toggle_minus");
- // Put a minus image
- $("#maincategory_img_" + id).attr("src", category_plus);
- }
- },
- zoomToCurrentExtent: function(){
- /* zoom to current extent */
- var current_extent = helpers.getSavedExtent();
- var extent;
- if (OpenLayers && current_extent && current_extent.length == 4){
- extent = new OpenLayers.Bounds(
- current_extent[0], current_extent[1],
- current_extent[2], current_extent[3]);
- }
- /*
- else if (OpenLayers && default_area && default_area.length == 4){
- extent = new OpenLayers.Bounds(default_area[0], default_area[1],
- default_area[2], default_area[3]);
- }*/
- else{
- return;
- }
- extent.transform(EPSG_DISPLAY_PROJECTION, EPSG_PROJECTION);
- settings.map.zoomToExtent(extent, true);
- return true;
- },
- // methods for edition
- setMarker: function (event){
- event = event || window.event;
- var lonlat = settings.map.getLonLatFromViewPortPx(event.xy);
- methods.putEditMarker(lonlat, false);
- OpenLayers.Event.stop(event);
- },
- /* put the marker on the map and update latitude and longitude fields */
- putEditMarker: function (lonlat, zoom){
- if (settings.current_edit_feature) {
- settings.layerMarkers.removeMarker(settings.current_edit_feature);
- }
- settings.current_edit_feature = new OpenLayers.Marker(lonlat.clone(),
- settings.default_icon);
- settings.layerMarkers.addMarker(settings.current_edit_feature);
- methods.updateMarkerInput();
- /* zoom to the point */
- if (zoom){
- var bounds = settings.layerMarkers.getDataExtent();
- if (bounds) settings.map.zoomToExtent(bounds);
- }
- return;
- },
- updateMarkerInput: function(){
- if (!settings.current_edit_feature) {
- return;
- }
- lonlat = settings.current_edit_feature.lonlat.clone().transform(
- settings.map.getProjectionObject(),
- EPSG_DISPLAY_PROJECTION);
- $('#id_point').val('POINT(' + lonlat.lon + ' ' + lonlat.lat + ')');
- if($('#live_latitude').length){
- $('#live_latitude').val(lonlat.lat);
- $('#live_longitude').val(lonlat.lon);
- }
-
- },
- updateRoutingInput: function(){
- if (!settings.current_route_feature) {
- return;
- }
- var current_geo = settings.current_route_feature.geometry.clone();
- current_geo.transform(EPSG_PROJECTION, EPSG_DISPLAY_PROJECTION);
- jQuery('#id_route').val(current_geo);
- },
- activateCurrentControl: function(){
- if (settings.current_control){
- settings.current_control.activate();
- } else {
- var pathCreate = settings.map.getControlsByClass(
- 'OpenLayers.Control.DrawFeature');
- if (pathCreate){
- pathCreate[0].activate();
- }
- }
- var pathModify = settings.map.getControlsByClass(
- 'OpenLayers.Control.ModifyFeature');
- if (settings.current_feature && pathModify){
- pathModify[0].selectControl.select(settings.current_feature);
- }
- },
- deactivateCurrentControl: function(){
- if (settings.current_control){
- settings.current_control.deactivate();
- }
- },
- initFeature: function(json_geometry){
- var json = new OpenLayers.Format.JSON();
- var polyline = json.read(json_geometry);
- var point_array = new Array();
- for (i=0; i{% endfor %}
{% if routing %}{% endif %}
-
-
{{ block.super }}
{% endblock %}
{% block message_edit %}{% endblock %}
diff --git a/chimere/widgets.py b/chimere/widgets.py
index f39248c..c2e8832 100644
--- a/chimere/widgets.py
+++ b/chimere/widgets.py
@@ -252,8 +252,7 @@ class PointChooserWidget(forms.TextInput):
["%schimere/css/forms.css" % settings.STATIC_URL,]
}
js = settings.MAP_JS_URLS[settings.CHIMERE_EDIT_RENDERER] + \
- list(settings.JQUERY_JS_URLS) + \
- ["%schimere/js/jquery.chimere.js" % settings.STATIC_URL]
+ list(settings.JQUERY_JS_URLS)
def render(self, name, value, attrs=None, area_name=''):
'''
@@ -347,8 +346,7 @@ class RouteChooserWidget(forms.TextInput):
}
js = settings.MAP_JS_URLS[settings.CHIMERE_EDIT_RENDERER] + \
list(settings.JQUERY_JS_URLS) + \
- ["%schimere/js/jquery.chimere.js" % settings.STATIC_URL,
- "%schimere/js/edit_route_map.js" % settings.STATIC_URL,
+ ["%schimere/js/edit_route_map.js" % settings.STATIC_URL,
"%schimere/js/base.js" % settings.STATIC_URL,]
def render(self, name, value, attrs=None, area_name='', routefile_id=None):
--
cgit v1.2.3
From a204849cb48d8acdaebeac484235c3a91fba00e6 Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Mon, 1 Apr 2013 18:52:14 +0200
Subject: Add a basic JS file to manage with leaflet
---
.../static/chimere/js/jquery.chimere-leaflet.js | 140 +++++++++++++++++++++
1 file changed, 140 insertions(+)
create mode 100644 chimere/static/chimere/js/jquery.chimere-leaflet.js
diff --git a/chimere/static/chimere/js/jquery.chimere-leaflet.js b/chimere/static/chimere/js/jquery.chimere-leaflet.js
new file mode 100644
index 0000000..75a0fc3
--- /dev/null
+++ b/chimere/static/chimere/js/jquery.chimere-leaflet.js
@@ -0,0 +1,140 @@
+/* Copyright (C) 2013 Étienne Loks
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+
+See the file COPYING for details.
+*/
+
+/*
+* Little hasattr helper
+*/
+(function ($) {
+ $.hasattr = function (key, arr) {
+ var v = arr[key];
+ if (typeof v === "undefined")
+ return false;
+ else
+ return v; };
+})( jQuery );
+
+(function ($) {
+ /*
+ * Chimere leaflet jQuery plugin
+ */
+ /*
+ * Default settings
+ */
+ var defaults = {
+ restricted_extent: false,
+ permalink_label: null,
+ permalink_div: null,
+ permalink: null, // OL Control, could be overrided
+ map_layers: null,
+ selected_map_layer: null,
+ dynamic_categories: false,
+ display_submited: false,
+ display_feature: null,
+ display_route: null,
+ area_id: null,
+ checked_categories: [],
+ zoom: null,
+ lat: null,
+ lon: null,
+ simple: false,
+ routing_start_lat: null,
+ routing_start_lon: null,
+ routing_end_lat: null,
+ routing_end_lon: null,
+ routing_steps_lonlat: null,
+ // Provide this function to make a custom click event on the marker
+ on_marker_click: null,
+ // Provide this function to override the feature detail display
+ display_feature_detail_fx: null,
+ // Provide this function for overriding the getSubcategories default
+ get_subcategories_fx: null,
+ hide_popup_fx: null,
+ // if leave to false every click on the map hide the pop-up
+ explicit_popup_hide: false,
+ popupClass: null,
+ popupContentFull: false, // if true the detail is inside the popup
+ category_accordion: true, // category opening behave like an accordion
+ maxResolution: 156543.0399,
+ units: 'm',
+ projection: 4326,
+ theme: null,
+ enable_clustering: false,
+ routing: false, // enable routing management
+ routing_panel_open: function(){
+ $('#chimere_itinerary_panel').dialog('open');
+ },
+ current_feature: null, // To store the active POI
+ current_control: null, // To store the current control
+ current_popup: null, // To store the current POI popup displayed
+ current_category: null, // To store the current category clicked in list
+ current_route_feature: null, // To store the current route find by routing
+ itinerary_step_number:0, // current step number
+ icon_offset: (0, 0),
+ edition: false, // edition mode
+ edition_type_is_route: false, // route or POI edition
+ default_icon: ('http://www.openlayers.org/dev/img/marker-green.png',
+ (21, 25), (-(21/2), -25)),
+ cluster_icon: null,
+ marker_hover_id:'marker_hover',
+ marker_hover_content_id:'marker_hover_content',
+ marker_hover_offset: null,
+ icon_start: null,
+ icon_step: null,
+ icon_end: null
+ };
+ var settings = {};
+ /*
+ * Publics methods
+ */
+ var methods = {
+ /*
+ * Plugin init function
+ */
+ init: function ( options ) {
+ } // end of init
+ }; // End of public methods
+ var helpers = {
+ }; // End of helpers
+
+ $.fn.chimere = function (thing) {
+ // Method calling logic
+ if ( methods[thing] ) {
+ return methods[ thing ].apply( this, Array.prototype.slice.call( arguments, 1 ));
+ }
+ else if ( typeof thing === 'object' || ! thing ) {
+ return methods.init.apply( this, arguments );
+ }
+ else if ( thing === 'settings' ) {
+ // Use $("#mydiv").chimere("settings", "key", "value") to change settings
+ // from outside the plugin
+ if (arguments.length == 3) {
+ settings[arguments[1]] = arguments[2];
+ }
+ else if (arguments.length == 2) {
+ return settings[arguments[1]];
+ }
+ else { // Use $("#mydiv").chimere("settings") to know the current settings
+ return settings;
+ }
+ }
+ else {
+ $.error( 'Method ' + thing + ' does not exist on jQuery.chimere' );
+ }
+ return this;
+ };
+})( jQuery );
--
cgit v1.2.3
From e3dd113da11a69f2107e6ac7fc5998626a23a2cc Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Wed, 3 Apr 2013 16:30:13 +0200
Subject: First work on Leaflet script
* manage markers from a GeoJson source with custom icon
---
chimere/settings.sample.py | 11 +-
.../static/chimere/js/jquery.chimere-leaflet.js | 371 +++++++++++++++++++++
chimere/static/chimere/js/jquery.chimere-ol.js | 67 ++--
chimere/templates/chimere/blocks/head_chimere.html | 7 +-
chimere/templates/chimere/blocks/map.html | 3 +-
chimere/templatetags/chimere_tags.py | 4 +-
6 files changed, 429 insertions(+), 34 deletions(-)
diff --git a/chimere/settings.sample.py b/chimere/settings.sample.py
index 2e5ad40..391c6e5 100644
--- a/chimere/settings.sample.py
+++ b/chimere/settings.sample.py
@@ -290,7 +290,8 @@ if 'MAP_JS_URLS' not in globals():
STATIC_URL + "openlayers/OpenLayers.js",
STATIC_URL + "openlayers/SimplePanZoom.js",
"http://www.openstreetmap.org/openlayers/OpenStreetMap.js",
- STATIC_URL + "chimere/js/jquery.chimere-ol.js"],
+ STATIC_URL + "chimere/js/jquery.chimere-ol.js"
+ ],
'leaflet':[
STATIC_URL + "leaflet/leaflet.js",
STATIC_URL + "chimere/js/jquery.chimere-leaflet.js"
@@ -299,11 +300,13 @@ if 'MAP_JS_URLS' not in globals():
if 'MAP_CSS_URLS' not in globals():
global MAP_CSS_URLS
+ # key: [(url, condition)]
MAP_CSS_URLS = {
- 'openlayers':["http://www.openlayers.org/api/theme/default/style.css"],
+ 'openlayers':[("http://www.openlayers.org/api/theme/default/style.css",
+ None)],
'leaflet':[
- STATIC_URL + "leaflet/leaflet.css",
- STATIC_URL + "leaflet/leaflet.ie.css"
+ (STATIC_URL + "leaflet/leaflet.css", None),
+ (STATIC_URL + "leaflet/leaflet.ie.css", 'lte IE 8')
]
}
diff --git a/chimere/static/chimere/js/jquery.chimere-leaflet.js b/chimere/static/chimere/js/jquery.chimere-leaflet.js
index 75a0fc3..569f9f5 100644
--- a/chimere/static/chimere/js/jquery.chimere-leaflet.js
+++ b/chimere/static/chimere/js/jquery.chimere-leaflet.js
@@ -106,9 +106,380 @@ See the file COPYING for details.
* Plugin init function
*/
init: function ( options ) {
+ settings = $.extend({}, defaults);
+ if ( options ) $.extend(settings, options);
+ var map_element = $(this).attr('id');
+
+ settings.map = map = L.map(map_element);
+ for (idx in settings.map_layers){
+ map.addLayer(settings.map_layers[idx]);
+ }
+ if(settings.zoom && settings.lat && settings.lon){
+ map.setView([settings.lat, settings.lon], settings.zoom);
+ } else {
+ map.fitWorld();
+ }
+ settings.icons = new Object();
+ settings.layerMarkers = L.geoJson(null, {
+ onEachFeature: function (feature, layer) {
+ layer.bindPopup(feature.properties.name);
+ },
+ pointToLayer: function (feature, latlng) {
+ if (feature.properties.weigthed){
+ var geojsonMarkerOptions = {
+ radius: 8,
+ fillColor: "#ff7800",
+ color: "#000",
+ weight: 1,
+ opacity: 1,
+ fillOpacity: 0.8
+ };
+ return L.circleMarker(latlng, geojsonMarkerOptions);
+ }
+ var icon_path = MEDIA_URL + feature.properties.icon_path;
+ if (!settings.icons.hasOwnProperty(icon_path)){
+ var icon_offset = null;
+ if (feature.properties.icon_offset){
+ icon_offset = feature.properties.icon_offset;
+ } else {
+ icon_offset = [feature.properties.icon_width/2,
+ feature.properties.icon_height];
+ }
+ var popup_anchor = null;
+ if (feature.properties.popup_anchor){
+ popup_anchor = feature.properties.popup_anchor;
+ } else {
+ popup_anchor = [0,
+ -feature.properties.icon_height];
+ }
+ settings.icons[icon_path] = L.icon({
+ iconUrl: icon_path,
+ iconSize: [feature.properties.icon_width,
+ feature.properties.icon_height],
+ iconAnchor: icon_offset,
+ popupAnchor: popup_anchor
+ });
+ }
+ return L.marker(latlng, {icon: settings.icons[icon_path]});
+ }
+ }).addTo(map);
+ settings.layerVectors = L.geoJson().addTo(map);
+
+ methods.loadCategories();
+ methods.loadGeoObjects();
+ },
+ hidePopup: function (evt) {
+ settings.map.closePopup();
+ return true;
+ },
+ /*
+ * Update the categories div in ajax
+ */
+ loadCategories: function () {
+ var current_extent = settings.map.getBounds();
+ current_extent = current_extent.toBBoxString();
+ current_extent = current_extent.split(',').join('_');
+ current_extent = current_extent.replace(/\./g, 'D');
+ current_extent = current_extent.replace(/-/g, 'M');
+ var uri = extra_url
+ if (settings.area_id) uri += settings.area_id + "/";
+ uri += "getAvailableCategories/";
+ var params = {"current_extent": current_extent}
+ if (settings.display_submited) params["status"] = "A_S";
+ $.ajax({url: uri,
+ data: params,
+ cache: false,
+ success: function (data) {
+ $('#categories').empty();
+ $('#categories').html(data);
+ _init_categories();
+ _reCheckCategories();
+ if (settings.current_category) {
+ // TODO : add a force mode
+ // (in case the category is yet visible in HTML...)
+ methods.toggle_category();
+ }
+ }
+ });
+ var _toggle_subcategories = function (category_element) {
+ // Check subcategories only if unchecked
+ var val = category_element.is(":checked") ? true : false;
+ category_element.parent().find("li input").attr("checked", val);
+ }
+ var _toggle_categories = function (subcategory_element) {
+ var parent = subcategory_element.closest('ul');
+ var parent_label = parent.parent().find("> span");
+ if (parent.find('input[type=checkbox]:checked').length){
+ parent_label.addClass('category-selected');
+ } else {
+ parent_label.removeClass('category-selected');
+ }
+ var master_check = parent.find("> input");
+ if (parent.find('.subcategories input[type=checkbox]').length ==
+ parent.find('.subcategories input[type=checkbox]:checked').length){
+ master_check.attr('checked', 'checked');
+ } else {
+ master_check.removeAttr('checked');
+ }
+
+ if($('#action-categories').length){
+ if ($('#categories input[type=checkbox]:checked').length){
+ $('#action-categories').addClass('category-selected');
+ } else {
+ $('#action-categories').removeClass('category-selected');
+ }
+ }
+ return master_check;
+ };
+ var _init_categories = function () {
+ /*
+ * Add event listener in categories DOM elements
+ */
+ $('#categories #ul_categories > li > input').bind("click",
+ function (e) {
+ methods.hidePopup(e);
+ _toggle_subcategories($(this));
+ methods.loadGeoObjects();
+ //settings.permalink.updateLink();
+ });
+ $('.subcategories li input').bind("click", function (e) {
+ var c_name = $(this).attr('name');
+ c_name = c_name.substr(c_name.lastIndexOf("_")+1);
+ if($(this).is(':checked')){
+ methods.subcategory_detail(c_name);
+ }
+ var par = $(this).parent();
+ if ($(this).attr('checked')){
+ par.addClass('selected');
+ } else {
+ par.removeClass('selected');
+ }
+ methods.hidePopup(e);
+ methods.loadGeoObjects();
+ _toggle_categories($(this));
+ //settings.permalink.updateLink();
+ if ($('#layer_cat_'+c_name).length){
+ $('#layer_cat_'+c_name).prop("checked",
+ this.checked);
+ }
+ });
+ $('#display_submited_check').bind("click", function () {
+ methods.loadGeoObjects();
+ //settings.permalink.updateLink();
+ });
+ // Zoom to category
+ $(".zoom_to_category").bind("click", function (e) {
+ var id = this.id.substr(this.id.lastIndexOf("_")+1);
+ helpers.zoom_to_category(id);
+ });
+ $(".zoom_to_subcategory").bind("click", function (e) {
+ var id = this.id.substr(this.id.lastIndexOf("_")+1);
+ helpers.zoom_to_subcategories([id]);
+ });
+ $(".toggle_category").parent().bind("click", function (e) {
+ var item = $(this).children('.toggle_category');
+ var id = item.attr('id').substr(item.attr('id').lastIndexOf("_")+1);
+ methods.toggle_category(id);
+ });
+ }
+ var _reCheckCategories = function (){
+ /* recheck categories on init or when a redraw occurs */
+ if (!settings.checked_categories){
+ return;
+ }
+ $('#frm_categories .subcategories input:checkbox').each(function(index){
+ cat_id = $(this).attr('id').split('_').pop();
+ if (settings.checked_categories.indexOf(parseInt(cat_id)) != -1) {
+ $(this).attr("checked", "checked");
+ _toggle_categories($(this));
+ methods.toggle_category();
+ } else {
+ $(this).attr("checked", false);
+ }
+ });
+ if (settings.display_submited == true){
+ $('#display_submited_check').attr("checked", "checked");
+ }
+ }
+ },
+ /*
+ * Load markers and route from DB
+ */
+ loadGeoObjects: function () {
+ if ($('#waiting').length){
+ $('#waiting').show();
+ }
+ helpers.retrieve_checked_categories();
+ var ids = settings.checked_categories.join('_');
+ if (!ids) ids = '0';
+ var uri = extra_url + "getGeoObjects/" + ids;
+ if (settings.display_submited) uri += "/A_S";
+ $.ajax({url: uri,
+ dataType: "json",
+ success: function (data) {
+ settings.layerMarkers.clearLayers();
+ settings.layerVectors.clearLayers();
+ settings.layerMarkers.addData(data);
+ /*
+ for (var i = 0; i < data.features.length; i++) {
+ var feature = data.features[i];
+ if (feature.geometry.type == 'Point'){
+ methods.addMarker(feature);
+ } else if (feature.geometry.type == 'LineString') {
+ methods.addRoute(feature);
+ } else if (feature.geometry.type == 'MultiLineString') {
+ methods.addMultiLine(feature);
+ }
+ }*/
+ },
+ error: function (data, textStatus, errorThrown) {
+ settings.layerMarkers.clearLayers();
+ settings.layerVectors.clearLayers();
+ },
+ complete: function () {
+ if($('#waiting').length){$('#waiting').hide();}
+ }
+ });
+ },
+ subcategory_detail: function(category_id){
+ var uri = extra_url + "getCategory/" + category_id;
+
+ $.ajax({url: uri,
+ dataType: "json",
+ success: function (data) {
+ if (!data.description){return}
+ $('#category_description').html(data.description);
+ $("#category_description").dialog("option", "title",
+ data.name);
+ $('#category_description').dialog('open');
+ },
+ error: function (data) {
+ // fail silently
+ }
+ });
+ },
+ toggle_category: function (id) {
+ // TODO make this id DOM element customisable
+ // Check if element is currently visible or not
+ var was_visible = $("#maincategory_" + id).is(":visible");
+ // Close all categories
+ var category_plus = STATIC_URL + "chimere/img/plus.png";
+ var category_minus = STATIC_URL + "chimere/img/minus.png";
+ if (settings.category_accordion){
+ $("#categories ul.subcategories").hide();
+ $("#categories img.toggle_category").attr("src", category_plus);
+ $("#categories .main_category").addClass("toggle_plus");
+ $("#categories .main_category").removeClass("toggle_minus");
+ }
+ // Put a minus image
+ if (!was_visible)
+ {
+ // Show the subcategories
+ $("#maincategory_" + id).toggle();
+ $("#maincategory_" + id).parent().addClass("toggle_minus");
+ $("#maincategory_" + id).parent().removeClass("toggle_plus");
+ // Put a plus image
+ $("#maincategory_img_" + id).attr("src", category_minus);
+ settings.current_category = id;
+ }
+ if (!settings.category_accordion && was_visible)
+ {
+ $("#maincategory_" + id).toggle();
+ $("#maincategory_" + id).parent().addClass("toggle_plus");
+ $("#maincategory_" + id).parent().removeClass("toggle_minus");
+ // Put a minus image
+ $("#maincategory_img_" + id).attr("src", category_plus);
+ }
+ },
+ zoom: function (options) {
+ if ($.hasattr("category", options)) {
+ helpers.zoom_to_category(options["category"]);
+ } else if ($.hasattr("subcategories", options)) {
+ helpers.zoom_to_subcategories(options["subcategories"]);
+ } else if ($.hasattr("area", options)) {
+ helpers.zoom_to_area(options["area"]);
+ }
} // end of init
}; // End of public methods
var helpers = {
+ getSubcategories: function (category_id) {
+ if(settings.get_subcategories_fx) {
+ return settings.get_subcategories_fx(category_id, settings);
+ }
+ else {
+ var ul = document.getElementById('maincategory_'+category_id);
+ var subcats = new Array();
+ for (i in ul.children){
+ var li = ul.children[i];
+ if (li.id){
+ subcats.push(li.id.split('_').pop());
+ }
+ }
+ return subcats;
+ }
+ },
+ retrieve_checked_categories: function () {
+ /*
+ * Retrieve checked_categories, and store it in settings
+ */
+ var initialized = false;
+ $('#frm_categories .subcategories input:checkbox').each(
+ function(index){
+ if (!initialized){
+ initialized = true;
+ settings.checked_categories = [];
+ settings.display_submited = false;
+ }
+ if ($(this).attr('checked') == 'checked' || $(this).attr('checked') == true){
+ cat_id = $(this).attr('id').split('_').pop();
+ settings.checked_categories.push(parseInt(cat_id));
+ }
+ });
+ if(initialized && ($('#display_submited_check').attr("checked") == "checked" || $('#display_submited_check').attr("checked") == true)){
+ settings.display_submited = true;
+ }
+ },
+ zoom_to: function (bounds) {
+ settings.map.fitBounds(bounds);
+ },
+ zoom_to_subcategories: function (ids) {
+ // TODO add markers and check the subcategory, if not yet checked/displayed
+ var ids = ids.join('_');
+ if (!ids) ids = '0';
+ var uri = extra_url + "getGeoObjects/" + ids;
+ if (settings.display_submited) uri += "/A_S";
+ $.ajax({url: uri,
+ dataType: "json",
+ success: function (data) {
+ // Create a generic bounds
+ var lon, lat, feature;
+ var points = new Array();
+ for (var i = 0; i < data.features.length; i++) {
+ feature = data.features[i];
+ if (feature.geometry.type == 'Point') {
+ lat = feature.geometry.coordinates[1];
+ lon = feature.geometry.coordinates[0];
+ points.push(settings.map.latLngToLayerPoint([lat, lon]));
+ } else if (feature.geometry.type == 'LineString') {
+ // TODO
+ }
+ }
+ var bound = L.Bounds(points);
+ helpers.zoom_to(bounds);
+ }
+ });
+ },
+ zoom_to_category: function (id) {
+ helpers.zoom_to_subcategories(helpers.getSubcategories(id));
+ },
+ zoom_to_area: function (coords) {
+ /* zoom to an area */
+ helpers.zoom_to([[coords[1], coords[0]],
+ [coords[3], coords[2]]]);
+ if (settings.dynamic_categories) {
+ methods.loadCategories();
+ }
+ }
}; // End of helpers
$.fn.chimere = function (thing) {
diff --git a/chimere/static/chimere/js/jquery.chimere-ol.js b/chimere/static/chimere/js/jquery.chimere-ol.js
index 2e2d238..6660b2e 100644
--- a/chimere/static/chimere/js/jquery.chimere-ol.js
+++ b/chimere/static/chimere/js/jquery.chimere-ol.js
@@ -17,20 +17,22 @@ See the file COPYING for details.
*/
/* Add OpenLayers MapQuest layer management */
-OpenLayers.Layer.MapQuestOSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {
- name: "MapQuestOSM",
- sphericalMercator: true,
- url: ' http://otile1.mqcdn.com/tiles/1.0.0/osm/${z}/${x}/${y}.png',
- clone: function(obj) {
- if (obj == null) {
- obj = new OpenLayers.Layer.OSM(
- this.name, this.url, this.getOptions());
- }
- obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);
- return obj;
- },
- CLASS_NAME: "OpenLayers.Layer.MapQuestOSM"
-});
+if (typeof(OpenLayers) != 'undefined'){
+ OpenLayers.Layer.MapQuestOSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {
+ name: "MapQuestOSM",
+ sphericalMercator: true,
+ url: ' http://otile1.mqcdn.com/tiles/1.0.0/osm/${z}/${x}/${y}.png',
+ clone: function(obj) {
+ if (obj == null) {
+ obj = new OpenLayers.Layer.OSM(
+ this.name, this.url, this.getOptions());
+ }
+ obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);
+ return obj;
+ },
+ CLASS_NAME: "OpenLayers.Layer.MapQuestOSM"
+ });
+};
/*
* Little hasattr helper
@@ -82,16 +84,13 @@ OpenLayers.Layer.MapQuestOSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {
hide_popup_fx: null,
// if leave to false every click on the map hide the pop-up
explicit_popup_hide: false,
- controls:[new OpenLayers.Control.Navigation(),
- new OpenLayers.Control.PanPanel(),
- new OpenLayers.Control.ZoomPanel(),
- new OpenLayers.Control.ScaleLine()],
- popupClass: OpenLayers.Popup.FramedCloud,
+ controls: null,
+ popupClass: null,
popupContentFull: false, // if true the detail is inside the popup
category_accordion: true, // category opening behave like an accordion
maxResolution: 156543.0399,
units: 'm',
- projection: new OpenLayers.Projection('EPSG:4326'),
+ projection: null,
theme: null,
enable_clustering: false,
routing: false, // enable routing management
@@ -104,13 +103,10 @@ OpenLayers.Layer.MapQuestOSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {
current_category: null, // To store the current category clicked in list
current_route_feature: null, // To store the current route find by routing
itinerary_step_number:0, // current step number
- icon_offset: new OpenLayers.Pixel(0, 0),
+ icon_offset: null,
edition: false, // edition mode
edition_type_is_route: false, // route or POI edition
- default_icon: new OpenLayers.Icon(
- 'http://www.openlayers.org/dev/img/marker-green.png',
- new OpenLayers.Size(21, 25),
- new OpenLayers.Pixel(-(21/2), -25)),
+ default_icon: null,
cluster_icon: null,
marker_hover_id:'marker_hover',
marker_hover_content_id:'marker_hover_content',
@@ -129,6 +125,27 @@ OpenLayers.Layer.MapQuestOSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {
*/
init: function ( options ) {
/* Manage parameters */
+ if (defaults.controls == null){
+ defaults.controls = [new OpenLayers.Control.Navigation(),
+ new OpenLayers.Control.PanPanel(),
+ new OpenLayers.Control.ZoomPanel(),
+ new OpenLayers.Control.ScaleLine()];
+ }
+ if (defaults.popupClass == null){
+ defaults.popupClass = OpenLayers.Popup.FramedCloud;
+ }
+ if (defaults.projection == null){
+ defaults.projection = new OpenLayers.Projection('EPSG:4326');
+ }
+ if (defaults.icon_offset == null){
+ defaults.icon_offset = new OpenLayers.Pixel(0, 0);
+ }
+ if (defaults.default_icon == null){
+ defaults.default_icon = new OpenLayers.Icon(
+ 'http://www.openlayers.org/dev/img/marker-green.png',
+ new OpenLayers.Size(21, 25),
+ new OpenLayers.Pixel(-(21/2), -25));
+ }
// not staticaly in default because of STATIC_URL init
if (defaults.cluster_icon == null && typeof STATIC_URL != 'undefined'){
defaults.cluster_icon = new OpenLayers.Icon(
diff --git a/chimere/templates/chimere/blocks/head_chimere.html b/chimere/templates/chimere/blocks/head_chimere.html
index 099a044..dea92ec 100644
--- a/chimere/templates/chimere/blocks/head_chimere.html
+++ b/chimere/templates/chimere/blocks/head_chimere.html
@@ -1,5 +1,5 @@
-{% for css_url in MAP_CSS_URLS %}
- {% endfor %}
+{% for css_url, condition in MAP_CSS_URLS %}
+{% if condition %}{%endif%}{% endfor %}
{% for js_url in MAP_JS_URLS %}
{% endfor %}
{% if routing %}{% endif %}
@@ -13,11 +13,12 @@
{% endfor %}
diff --git a/chimere/templatetags/chimere_tags.py b/chimere/templatetags/chimere_tags.py
index 90252a1..47f0800 100644
--- a/chimere/templatetags/chimere_tags.py
+++ b/chimere/templatetags/chimere_tags.py
@@ -144,7 +144,7 @@ def head_chimere(context, view=True):
"DEFAULT_ZOOM": settings.CHIMERE_DEFAULT_ZOOM,
"MAP_LAYER": settings.CHIMERE_DEFAULT_MAP_LAYER,
"CHIMERE_VIEW_RENDERER": settings.CHIMERE_VIEW_RENDERER,
- "MAP_CSS_URLS": settings.MAP_CSS_URLS[map_renderer],
+ "MAP_CONDITIONNAL_CSS_URLS": settings.MAP_CONDITIONNAL_CSS_URLS[map_renderer],
"MAP_JS_URLS": settings.MAP_JS_URLS[map_renderer],
'routing': settings.CHIMERE_ENABLE_ROUTING \
if hasattr(settings, 'CHIMERE_ENABLE_ROUTING') else False
--
cgit v1.2.3
From 1ab25dfc2603d6145a7ca6100558e470761ce766 Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Fri, 5 Apr 2013 13:22:09 +0200
Subject: Weighted markers: model
---
chimere/admin.py | 3 +-
chimere/forms.py | 2 +-
...arker_weight__add_field_subcategory_weighted.py | 288 +++++++++++++++++++++
chimere/models.py | 8 +
chimere/widgets.py | 2 +-
5 files changed, 299 insertions(+), 4 deletions(-)
create mode 100644 chimere/migrations/0004_auto__add_field_marker_weight__add_field_subcategory_weighted.py
diff --git a/chimere/admin.py b/chimere/admin.py
index c9107a5..c36fd26 100644
--- a/chimere/admin.py
+++ b/chimere/admin.py
@@ -192,7 +192,7 @@ class MarkerAdmin(admin.ModelAdmin):
form = MarkerAdminForm
fieldsets = ((None, {
'fields': ['point', 'name', 'status', 'categories',
- 'description', 'start_date', 'end_date']
+ 'description', 'weight', 'start_date', 'end_date']
}),
(_(u"Submitter"), {
'classes':('collapse',),
@@ -254,7 +254,6 @@ class MarkerAdmin(admin.ModelAdmin):
)
return my_urls + urls
-
class RouteAdmin(MarkerAdmin):
"""
Specialized the Route field.
diff --git a/chimere/forms.py b/chimere/forms.py
index 655bbd3..ea23f94 100644
--- a/chimere/forms.py
+++ b/chimere/forms.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-# Copyright (C) 2008-2012 Étienne Loks
+# Copyright (C) 2008-2013 Étienne Loks
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as
diff --git a/chimere/migrations/0004_auto__add_field_marker_weight__add_field_subcategory_weighted.py b/chimere/migrations/0004_auto__add_field_marker_weight__add_field_subcategory_weighted.py
new file mode 100644
index 0000000..427bf86
--- /dev/null
+++ b/chimere/migrations/0004_auto__add_field_marker_weight__add_field_subcategory_weighted.py
@@ -0,0 +1,288 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding field 'Marker.weight'
+ db.add_column('chimere_marker', 'weight',
+ self.gf('django.db.models.fields.IntegerField')(default=0, null=True, blank=True),
+ keep_default=False)
+
+ # Adding field 'SubCategory.weighted'
+ db.add_column('chimere_subcategory', 'weighted',
+ self.gf('django.db.models.fields.BooleanField')(default=False),
+ keep_default=False)
+
+
+ def backwards(self, orm):
+ # Deleting field 'Marker.weight'
+ db.delete_column('chimere_marker', 'weight')
+
+ # Deleting field 'SubCategory.weighted'
+ db.delete_column('chimere_subcategory', 'weighted')
+
+
+ models = {
+ 'chimere.aggregatedroute': {
+ 'Meta': {'object_name': 'AggregatedRoute', 'db_table': "'chimere_aggregated_routes'", 'managed': 'False'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'route': ('django.contrib.gis.db.models.fields.MultiLineStringField', [], {}),
+ 'status': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'subcategory': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.SubCategory']"})
+ },
+ 'chimere.area': {
+ 'Meta': {'ordering': "('order', 'name')", 'object_name': 'Area'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'default': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'default_subcategories': ('chimere.widgets.SelectMultipleField', [], {'to': "orm['chimere.SubCategory']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'dynamic_categories': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'external_css': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layers': ('chimere.widgets.SelectMultipleField', [], {'symmetrical': 'False', 'related_name': "'areas'", 'blank': 'True', 'through': "orm['chimere.AreaLayers']", 'to': "orm['chimere.Layer']"}),
+ 'lower_right_corner': ('django.contrib.gis.db.models.fields.PointField', [], {'default': "'POINT(0 0)'"}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'unique': 'True'}),
+ 'restrict_to_extent': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'subcategories': ('chimere.widgets.SelectMultipleField', [], {'symmetrical': 'False', 'related_name': "'areas'", 'blank': 'True', 'db_table': "'chimere_subcategory_areas'", 'to': "orm['chimere.SubCategory']"}),
+ 'upper_left_corner': ('django.contrib.gis.db.models.fields.PointField', [], {'default': "'POINT(0 0)'"}),
+ 'urn': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'blank': 'True'}),
+ 'welcome_message': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.arealayers': {
+ 'Meta': {'ordering': "('order',)", 'object_name': 'AreaLayers'},
+ 'area': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.Area']"}),
+ 'default': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.Layer']"}),
+ 'order': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.category': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'Category'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.color': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'Color'},
+ 'code': ('django.db.models.fields.CharField', [], {'max_length': '6'}),
+ 'color_theme': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.ColorTheme']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.colortheme': {
+ 'Meta': {'object_name': 'ColorTheme'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.icon': {
+ 'Meta': {'object_name': 'Icon'},
+ 'height': ('django.db.models.fields.IntegerField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'width': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.importer': {
+ 'Meta': {'object_name': 'Importer'},
+ 'associate_marker_to_way': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'categories': ('chimere.widgets.SelectMultipleField', [], {'to': "orm['chimere.SubCategory']", 'symmetrical': 'False'}),
+ 'default_description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'default_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'filtr': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'get_description': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'importer_type': ('django.db.models.fields.CharField', [], {'max_length': '4'}),
+ 'license': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'origin': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'overwrite': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'source': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'source_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'srid': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'state': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'zipped': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'chimere.layer': {
+ 'Meta': {'object_name': 'Layer'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layer_code': ('django.db.models.fields.TextField', [], {'max_length': '300'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.marker': {
+ 'Meta': {'ordering': "('status', 'name')", 'object_name': 'Marker'},
+ 'available_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'categories': ('chimere.widgets.SelectMultipleField', [], {'to': "orm['chimere.SubCategory']", 'symmetrical': 'False'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'end_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'import_key': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'import_source': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'import_version': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'is_front_page': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'license': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'modified_since_import': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'not_for_osm': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'origin': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'point': ('chimere.widgets.PointField', [], {}),
+ 'ref_item': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'submited_marker'", 'null': 'True', 'to': "orm['chimere.Marker']"}),
+ 'route': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'associated_marker'", 'null': 'True', 'to': "orm['chimere.Route']"}),
+ 'start_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'status': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'submiter_comment': ('django.db.models.fields.TextField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'submiter_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
+ 'submiter_name': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
+ 'submiter_session_key': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
+ 'weight': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.multimediaextension': {
+ 'Meta': {'object_name': 'MultimediaExtension'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'multimedia_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'extensions'", 'to': "orm['chimere.MultimediaType']"}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '6'})
+ },
+ 'chimere.multimediafile': {
+ 'Meta': {'object_name': 'MultimediaFile'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'marker': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'multimedia_files'", 'to': "orm['chimere.Marker']"}),
+ 'miniature': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'multimedia_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.MultimediaType']", 'null': 'True', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'url': ('django.db.models.fields.URLField', [], {'max_length': '200'})
+ },
+ 'chimere.multimediatype': {
+ 'Meta': {'object_name': 'MultimediaType'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'iframe': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'media_type': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'mime_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.news': {
+ 'Meta': {'object_name': 'News'},
+ 'areas': ('chimere.widgets.SelectMultipleField', [], {'symmetrical': 'False', 'to': "orm['chimere.Area']", 'null': 'True', 'blank': 'True'}),
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'content': ('django.db.models.fields.TextField', [], {}),
+ 'date': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_front_page': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.page': {
+ 'Meta': {'object_name': 'Page'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'content': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mnemonic': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '10', 'null': 'True', 'blank': 'True'}),
+ 'template_path': ('django.db.models.fields.CharField', [], {'max_length': '150', 'null': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.picturefile': {
+ 'Meta': {'object_name': 'PictureFile'},
+ 'height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'marker': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'pictures'", 'to': "orm['chimere.Marker']"}),
+ 'miniature': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'picture': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
+ 'thumbnailfile': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'thumbnailfile_height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'thumbnailfile_width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.property': {
+ 'Meta': {'object_name': 'Property'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'marker': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.Marker']"}),
+ 'propertymodel': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.PropertyModel']"}),
+ 'value': ('django.db.models.fields.TextField', [], {})
+ },
+ 'chimere.propertymodel': {
+ 'Meta': {'ordering': "('order',)", 'object_name': 'PropertyModel'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mandatory': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {}),
+ 'subcategories': ('chimere.widgets.SelectMultipleField', [], {'symmetrical': 'False', 'related_name': "'properties'", 'blank': 'True', 'to': "orm['chimere.SubCategory']"}),
+ 'type': ('django.db.models.fields.CharField', [], {'max_length': '1'})
+ },
+ 'chimere.propertymodelchoice': {
+ 'Meta': {'object_name': 'PropertyModelChoice'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'propertymodel': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'choices'", 'to': "orm['chimere.PropertyModel']"}),
+ 'value': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.route': {
+ 'Meta': {'ordering': "('status', 'name')", 'object_name': 'Route'},
+ 'associated_file': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.RouteFile']", 'null': 'True', 'blank': 'True'}),
+ 'categories': ('chimere.widgets.SelectMultipleField', [], {'to': "orm['chimere.SubCategory']", 'symmetrical': 'False'}),
+ 'end_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'has_associated_marker': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'import_key': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'import_source': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'import_version': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'license': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'modified_since_import': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'not_for_osm': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'origin': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'picture': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'ref_item': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'submited_route'", 'null': 'True', 'to': "orm['chimere.Route']"}),
+ 'route': ('chimere.widgets.RouteField', [], {}),
+ 'start_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'status': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'submiter_comment': ('django.db.models.fields.TextField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'submiter_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
+ 'submiter_name': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
+ 'submiter_session_key': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
+ 'width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.routefile': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'RouteFile'},
+ 'file_type': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'raw_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+ 'simplified_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.subcategory': {
+ 'Meta': {'ordering': "['category', 'order']", 'object_name': 'SubCategory'},
+ 'as_layer': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'category': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'subcategories'", 'to': "orm['chimere.Category']"}),
+ 'color_theme': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.ColorTheme']", 'null': 'True', 'blank': 'True'}),
+ 'dated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'hover_icon': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'subcat_hovered'", 'null': 'True', 'to': "orm['chimere.Icon']"}),
+ 'icon': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.Icon']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'item_type': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '1000'}),
+ 'routing_warn': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'submission': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'weighted': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'chimere.tinyurl': {
+ 'Meta': {'object_name': 'TinyUrl'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'parameters': ('django.db.models.fields.CharField', [], {'max_length': '500'})
+ }
+ }
+
+ complete_apps = ['chimere']
\ No newline at end of file
diff --git a/chimere/models.py b/chimere/models.py
index 545d7e0..3400118 100644
--- a/chimere/models.py
+++ b/chimere/models.py
@@ -213,6 +213,8 @@ class SubCategory(models.Model):
available = models.BooleanField(_(u"Available"), default=True)
submission = models.BooleanField(_(u"Available for submission"),
default=True)
+ weighted = models.BooleanField(_(u"Has an associated quantity"),
+ default=False)
TYPE = (('M', _(u'Marker')),
('R', _(u'Route')),
('B', _(u'Both')),)
@@ -465,6 +467,8 @@ class Marker(GeographicItem):
null=True) # used by feeds
route = models.ForeignKey(u"Route", blank=True, null=True,
related_name='associated_marker')
+ weight = models.IntegerField(_(u"Quantity"), blank=True, null=True,
+ default=0)
description = models.TextField(_(u"Description"), blank=True, null=True)
is_front_page = models.NullBooleanField(_(u"Is front page"), blank=True,
null=True)
@@ -534,6 +538,10 @@ class Marker(GeographicItem):
def geom_attr(self):
return 'point'
+ @property
+ def has_weight(self):
+ return bool(self.categories.filter(weighted=True).count())
+
class Meta:
ordering = ('status', 'name')
verbose_name = _(u"Point of interest")
diff --git a/chimere/widgets.py b/chimere/widgets.py
index c2e8832..05752ce 100644
--- a/chimere/widgets.py
+++ b/chimere/widgets.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-# Copyright (C) 2008-2012 Étienne Loks
+# Copyright (C) 2008-2013 Étienne Loks
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as
--
cgit v1.2.3
From 426eb4711ff92cc0e6df9cd1004ff322f112c814 Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Fri, 5 Apr 2013 14:28:35 +0200
Subject: Weighted markers: first display with Leaflet
---
chimere/models.py | 17 ++++++++++++++---
chimere/static/chimere/js/jquery.chimere-leaflet.js | 17 +++++++++++++----
2 files changed, 27 insertions(+), 7 deletions(-)
diff --git a/chimere/models.py b/chimere/models.py
index 3400118..c269439 100644
--- a/chimere/models.py
+++ b/chimere/models.py
@@ -171,7 +171,8 @@ class Color(models.Model):
"""
code = models.CharField(_(u"Code"), max_length=6)
order = models.IntegerField(_(u"Order"))
- color_theme = models.ForeignKey(ColorTheme, verbose_name=_(u"Color theme"))
+ color_theme = models.ForeignKey(ColorTheme, verbose_name=_(u"Color theme"),
+ related_name='colors')
def __unicode__(self):
return self.code
class Meta:
@@ -637,14 +638,24 @@ class Marker(GeographicItem):
if cat.hover_icon else '',
'icon_width':cat.icon.image.width,
'icon_height':cat.icon.image.height,
- 'category_name':json.dumps(cat.name)}
+ 'category_name':json.dumps(cat.name),}
+ items['weight'] = ''
+ if cat.weighted:
+ if not self.weight:
+ continue
+ items['weight'] = u', "weight":%d' % self.weight
+ if cat.color_theme and cat.color_theme.colors.count():
+ items['weight'] += u', "colors":["#%s"]' % '", "#'.join(
+ [color.code for color in cat.color_theme.colors.\
+ order_by('order').all()])
jsons.append(u'{"type":"Feature", "geometry":%(geometry)s, '\
u'"properties":{"pk": %(id)d, "name": %(name)s, '\
u'"icon_path":"%(icon_path)s", '\
u'"icon_hover_path":"%(icon_hover_path)s", '\
u'"icon_width":%(icon_width)d, '\
u'"icon_height":%(icon_height)d, '\
- u'"category_name":%(category_name)s}}' % items)
+ u'"category_name":%(category_name)s'\
+ u'%(weight)s}}' % items)
return ",".join(jsons)
@property
diff --git a/chimere/static/chimere/js/jquery.chimere-leaflet.js b/chimere/static/chimere/js/jquery.chimere-leaflet.js
index 569f9f5..5e0e811 100644
--- a/chimere/static/chimere/js/jquery.chimere-leaflet.js
+++ b/chimere/static/chimere/js/jquery.chimere-leaflet.js
@@ -125,12 +125,21 @@ See the file COPYING for details.
layer.bindPopup(feature.properties.name);
},
pointToLayer: function (feature, latlng) {
- if (feature.properties.weigthed){
+ if (feature.properties.weight){
+ var fill_color = "#ff7800";
+ if (feature.properties.colors){
+ var idx = feature.properties.weight/5;
+ if (idx < feature.properties.colors.length){
+ fill_color = feature.properties.colors[idx];
+ } else {
+ fill_color = feature.properties.colors[feature.properties.colors.length-1];
+ }
+ }
var geojsonMarkerOptions = {
- radius: 8,
- fillColor: "#ff7800",
+ radius: feature.properties.weight*2,
+ fillColor: fill_color,
color: "#000",
- weight: 1,
+ weight: feature.properties.weight/2,
opacity: 1,
fillOpacity: 0.8
};
--
cgit v1.2.3
From 2e9154a16b03c586450e15ec931996db3254f9fd Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Fri, 5 Apr 2013 18:06:06 +0200
Subject: Leaflet: work on clustering
---
chimere/settings.sample.py | 19 ++-
.../static/chimere/js/jquery.chimere-leaflet.js | 151 +++++++++++++--------
chimere/static/leaflet-markercluster/CHANGELOG.md | 49 +++++++
.../static/leaflet-markercluster/MIT-LICENCE.txt | 20 +++
.../MarkerCluster.Default.css | 38 ++++++
.../MarkerCluster.Default.ie.css | 22 +++
.../static/leaflet-markercluster/MarkerCluster.css | 6 +
chimere/static/leaflet-markercluster/README.md | 125 +++++++++++++++++
.../leaflet-markercluster/leaflet.markercluster.js | 6 +
chimere/templates/chimere/blocks/map.html | 1 +
chimere/templatetags/chimere_tags.py | 4 +
11 files changed, 381 insertions(+), 60 deletions(-)
create mode 100644 chimere/static/leaflet-markercluster/CHANGELOG.md
create mode 100644 chimere/static/leaflet-markercluster/MIT-LICENCE.txt
create mode 100644 chimere/static/leaflet-markercluster/MarkerCluster.Default.css
create mode 100644 chimere/static/leaflet-markercluster/MarkerCluster.Default.ie.css
create mode 100644 chimere/static/leaflet-markercluster/MarkerCluster.css
create mode 100644 chimere/static/leaflet-markercluster/README.md
create mode 100644 chimere/static/leaflet-markercluster/leaflet.markercluster.js
diff --git a/chimere/settings.sample.py b/chimere/settings.sample.py
index da703fb..b23f31e 100644
--- a/chimere/settings.sample.py
+++ b/chimere/settings.sample.py
@@ -76,6 +76,8 @@ CHIMERE_SHAPEFILE_ENCODING = 'ISO-8859-1'
# it could be a good idea to hide it to an admin who could'nt do that
CHIMERE_HIDE_PROPERTYMODEL = False
+CHIMERE_ENABLE_CLUSTERING = False
+
# enable routing in Chimère
CHIMERE_ENABLE_ROUTING = False
@@ -297,13 +299,21 @@ if 'MAP_JS_URLS' not in globals():
STATIC_URL + "chimere/js/jquery.chimere-leaflet.js"
]
}
-
+ if CHIMERE_ENABLE_CLUSTERING:
+ MAP_JS_URLS['leaflet'] += [
+ STATIC_URL + "leaflet-markercluster/leaflet.markercluster.js"
+ ]
if 'MAP_CSS_URLS' not in globals():
global MAP_CSS_URLS
MAP_CSS_URLS = {
'openlayers':["http://www.openlayers.org/api/theme/default/style.css"],
'leaflet':[STATIC_URL + "leaflet/leaflet.css"],
}
+ if CHIMERE_ENABLE_CLUSTERING:
+ MAP_CSS_URLS['leaflet'] += [
+ STATIC_URL + "leaflet-markercluster/MarkerCluster.css",
+ STATIC_URL + "leaflet-markercluster/MarkerCluster.Default.css",
+ ]
# key: [(url, condition)]
MAP_CONDITIONNAL_CSS_URLS = {
'openlayers':[("http://www.openlayers.org/api/theme/default/style.css",
@@ -313,4 +323,11 @@ if 'MAP_CSS_URLS' not in globals():
(STATIC_URL + "leaflet/leaflet.ie.css", 'lte IE 8')
]
}
+ if CHIMERE_ENABLE_CLUSTERING:
+ MAP_CSS_URLS['leaflet'] += [
+ (STATIC_URL + "leaflet-markercluster/MarkerCluster.css", None),
+ (STATIC_URL + "leaflet-markercluster/MarkerCluster.Default.css", None),
+ (STATIC_URL + "leaflet-markercluster/MarkerCluster.Default.ie.css",
+ 'lte IE 8'),
+ ]
diff --git a/chimere/static/chimere/js/jquery.chimere-leaflet.js b/chimere/static/chimere/js/jquery.chimere-leaflet.js
index 5e0e811..36729c1 100644
--- a/chimere/static/chimere/js/jquery.chimere-leaflet.js
+++ b/chimere/static/chimere/js/jquery.chimere-leaflet.js
@@ -120,58 +120,77 @@ See the file COPYING for details.
map.fitWorld();
}
settings.icons = new Object();
- settings.layerMarkers = L.geoJson(null, {
- onEachFeature: function (feature, layer) {
- layer.bindPopup(feature.properties.name);
- },
- pointToLayer: function (feature, latlng) {
- if (feature.properties.weight){
- var fill_color = "#ff7800";
- if (feature.properties.colors){
- var idx = feature.properties.weight/5;
- if (idx < feature.properties.colors.length){
- fill_color = feature.properties.colors[idx];
- } else {
- fill_color = feature.properties.colors[feature.properties.colors.length-1];
- }
+ if (settings.enable_clustering){
+ settings.layerMarkers = new L.MarkerClusterGroup({
+ iconCreateFunction: function (cluster) {
+ var markers = cluster.getAllChildMarkers();
+ var weight = 0;
+ for (idx=0;idx' + cluster.getChildCount() + '' });
+ }
+});
+```
+Check out the [custom example](http://leaflet.github.com/Leaflet.markercluster/example/marker-clustering-custom.html) for an example of this.
+
+### All Options
+Enabled by default (boolean options):
+* **zoomToBoundsOnClick**: When you click a cluster we zoom to its bounds.
+* **showCoverageOnHover**: When you mouse over a cluster it shows the bounds of its markers.
+* **spiderfyOnMaxZoom**: When you click a cluster at the bottom zoom level we spiderfy it so you can see all of its markers.
+
+Other options
+* **animateAddingMarkers**: If set to true then adding individual markers to the MarkerClusterGroup after it has been added to the map will add the marker and animate it in to the cluster. Defaults to false as this gives better performance when bulk adding markers.
+* **disableClusteringAtZoom**: If set, at this zoom level and below markers will not be clustered. This defaults to disabled. [See Example](http://leaflet.github.com/Leaflet.markercluster/example/marker-clustering-realworld-maxzoom.388.html)
+* **maxClusterRadius**: The maximum radius that a cluster will cover from the central marker (in pixels). Default 80. Decreasing will make more smaller clusters.
+* **polygonOptions**: Options to pass when creating the L.Polygon to show the bounds of a cluster
+* **singleMarkerMode**: If set to true, overrides the icon for all added markers to make them appear as a 1 size cluster
+* **spiderfyDistanceMultiplier**: Increase from 1 to increase the distance away from the center that spiderfied markers are placed. Use if you are using big marker icons.
+
+## Events
+If you register for click, mouseover, etc events just related to Markers in the cluster.
+To recieve events for clusters listen to 'cluster' + 'eventIWant', ex: 'clusterclick', 'clustermouseover'.
+
+Set your callback up as follows to handle both cases:
+
+```javascript
+markers.on('click', function (a) {
+ console.log('marker ' + a.layer);
+});
+
+markers.on('clusterclick', function (a) {
+ console.log('cluster ' + a.layer.getAllChildMarkers().length);
+});
+```
+
+## Methods
+
+### Getting the bounds of a cluster
+When you recieve an event from a cluster you can query it for the bounds.
+See [example/marker-clustering-convexhull.html](http://leaflet.github.com/Leaflet.markercluster/example/marker-clustering-convexhull.html) for a working example.
+```javascript
+markers.on('clusterclick', function (a) {
+ map.addLayer(new L.Polygon(a.layer.getConvexHull()));
+});
+```
+
+### Zooming to the bounds of a cluster
+When you recieve an event from a cluster you can zoom to its bounds in one easy step.
+See [marker-clustering-zoomtobounds.html](http://leaflet.github.com/Leaflet.markercluster/example/marker-clustering-zoomtobounds.html) for a working example.
+```javascript
+markers.on('clusterclick', function (a) {
+ a.layer.zoomToBounds();
+});
+```
+
+### Adding and removing Markers
+addLayer, removeLayer and clearLayers are supported and they should work for most uses.
+
+### Bulk adding and removing Markers
+addLayers and removeLayers are bulk methods for adding and removing markers and should be favoured over the single versions when doing bulk addition/removal of markers. Each takes an array of markers
+
+If you are removing a lot of markers it will almost definitely be better to call clearLayers then call addLayers to add the markers you don't want to remove back in. See [#59](https://github.com/Leaflet/Leaflet.markercluster/issues/59#issuecomment-9320628) for details.
+
+### Other Methods
+````
+hasLayer(layer): Returns true if the given layer (marker) is in the MarkerClusterGroup
+zoomToShowLayer(layer, callback): Zooms to show the given marker (spidifying if required), calls the callback when the marker is visible on the map
+addLayers(layerArray): Adds the markers in the given array from the MarkerClusterGroup in an efficent bulk method.
+removeLayers(layerArray): Removes the markers in the given array from the MarkerClusterGroup in an efficent bulk method.
+````
+
+## Handling LOTS of markers
+The Clusterer can handle 10000 or even 50000 markers (in chrome). IE9 has some issues with 50000.
+[realworld 10000 example](http://leaflet.github.com/Leaflet.markercluster/example/marker-clustering-realworld.10000.html)
+[realworld 50000 example](http://leaflet.github.com/Leaflet.markercluster/example/marker-clustering-realworld.50000.html)
+Performance optimizations could be done so these are handled more gracefully (Running the initial clustering over multiple JS calls rather than locking the browser for a long time)
+
+### License
+
+Leaflet.markercluster is free software, and may be redistributed under the MIT-LICENSE.
diff --git a/chimere/static/leaflet-markercluster/leaflet.markercluster.js b/chimere/static/leaflet-markercluster/leaflet.markercluster.js
new file mode 100644
index 0000000..74c25c3
--- /dev/null
+++ b/chimere/static/leaflet-markercluster/leaflet.markercluster.js
@@ -0,0 +1,6 @@
+/*
+ Copyright (c) 2012, Smartrak, David Leaver
+ Leaflet.markercluster is an open-source JavaScript library for Marker Clustering on leaflet powered maps.
+ https://github.com/danzel/Leaflet.markercluster
+*/
+(function(e,t){L.MarkerClusterGroup=L.FeatureGroup.extend({options:{maxClusterRadius:80,iconCreateFunction:null,spiderfyOnMaxZoom:!0,showCoverageOnHover:!0,zoomToBoundsOnClick:!0,singleMarkerMode:!1,disableClusteringAtZoom:null,removeOutsideVisibleBounds:!0,animateAddingMarkers:!1,spiderfyDistanceMultiplier:1,polygonOptions:{}},initialize:function(e){L.Util.setOptions(this,e),this.options.iconCreateFunction||(this.options.iconCreateFunction=this._defaultIconCreateFunction),L.FeatureGroup.prototype.initialize.call(this,[]),this._inZoomAnimation=0,this._needsClustering=[],this._currentShownBounds=null},addLayer:function(e){if(e instanceof L.LayerGroup){var t=[];for(var n in e._layers)e._layers.hasOwnProperty(n)&&t.push(e._layers[n]);return this.addLayers(t)}if(!this._map)return this._needsClustering.push(e),this;if(this.hasLayer(e))return this;this._unspiderfy&&this._unspiderfy(),this._addLayer(e,this._maxZoom);var r=e,i=this._map.getZoom();if(e.__parent)while(r.__parent._zoom>=i)r=r.__parent;return this._currentShownBounds.contains(r.getLatLng())&&(this.options.animateAddingMarkers?this._animationAddLayer(e,r):this._animationAddLayerNonAnimated(e,r)),this},removeLayer:function(e){return this._map?e.__parent?(this._unspiderfy&&(this._unspiderfy(),this._unspiderfyLayer(e)),this._removeLayer(e,!0),e._icon&&(L.FeatureGroup.prototype.removeLayer.call(this,e),e.setOpacity(1)),this):this:(this._arraySplice(this._needsClustering,e),this)},addLayers:function(e){var t,n,r;if(!this._map)return this._needsClustering=this._needsClustering.concat(e),this;for(t=0,n=e.length;t=0;t--)e.extend(this._needsClustering[t].getLatLng());return e},eachLayer:function(e,t){var n=this._needsClustering.slice(),r;this._topClusterLevel&&this._topClusterLevel.getAllChildMarkers(n);for(r=n.length-1;r>=0;r--)e.call(t,n[r])},hasLayer:function(e){if(this._needsClustering.length>0){var t=this._needsClustering;for(var n=t.length-1;n>=0;n--)if(t[n]===e)return!0}return!!e.__parent&&e.__parent._group===this},zoomToShowLayer:function(e,t){var n=function(){if((e._icon||e.__parent._icon)&&!this._inZoomAnimation){this._map.off("moveend",n,this),this.off("animationend",n,this);if(e._icon)t();else if(e.__parent._icon){var r=function(){this.off("spiderfied",r,this),t()};this.on("spiderfied",r,this),e.__parent.spiderfy()}}};e._icon?t():e.__parent._zoom=0;n--)if(e[n]===t){e.splice(n,1);return}},_removeLayer:function(e,t,n){var r=this._gridClusters,i=this._gridUnclustered,s=this._map;if(t)for(var o=this._maxZoom;o>=0;o--)if(!i[o].removeObject(e,s.project(e.getLatLng(),o)))break;var u=e.__parent,a=u._markers,f;this._arraySplice(a,e);while(u){u._childCount--;if(u._zoom<0)break;t&&u._childCount<=1?(f=u._markers[0]===e?u._markers[1]:u._markers[0],r[u._zoom].removeObject(u,s.project(u._cLatLng,u._zoom)),i[u._zoom].addObject(f,s.project(f.getLatLng(),u._zoom)),this._arraySplice(u.__parent._childClusters,u),u.__parent._markers.push(f),f.__parent=u.__parent,u._icon&&(L.FeatureGroup.prototype.removeLayer.call(this,u),n||L.FeatureGroup.prototype.addLayer.call(this,f))):(u._recalculateBounds(),(!n||!u._icon)&&u._updateIcon()),u=u.__parent}delete e.__parent},_propagateEvent:function(e){e.target instanceof L.MarkerCluster&&(e.type="cluster"+e.type),L.FeatureGroup.prototype._propagateEvent.call(this,e)},_defaultIconCreateFunction:function(e){var t=e.getChildCount(),n=" marker-cluster-";return t<10?n+="small":t<100?n+="medium":n+="large",new L.DivIcon({html:""+t+"
",className:"marker-cluster"+n,iconSize:new L.Point(40,40)})},_bindEvents:function(){var e=null,t=this._map,n=this.options.spiderfyOnMaxZoom,r=this.options.showCoverageOnHover,i=this.options.zoomToBoundsOnClick;(n||i)&&this.on("clusterclick",function(e){t.getMaxZoom()===t.getZoom()?n&&e.layer.spiderfy():i&&e.layer.zoomToBounds()},this),r&&(this.on("clustermouseover",function(n){if(this._inZoomAnimation)return;e&&t.removeLayer(e),n.layer.getChildCount()>2&&n.layer!==this._spiderfied&&(e=new L.Polygon(n.layer.getConvexHull(),this.options.polygonOptions),t.addLayer(e))},this),this.on("clustermouseout",function(){e&&(t.removeLayer(e),e=null)},this),t.on("zoomend",function(){e&&(t.removeLayer(e),e=null)},this),t.on("layerremove",function(n){e&&n.layer===this&&(t.removeLayer(e),e=null)},this))},_unbindEvents:function(){var e=this.options.spiderfyOnMaxZoom,t=this.options.showCoverageOnHover,n=this.options.zoomToBoundsOnClick,r=this._map;(e||n)&&this.off("clusterclick",null,this),t&&(this.off("clustermouseover",null,this),this.off("clustermouseout",null,this),r.off("zoomend",null,this),r.off("layerremove",null,this))},_zoomEnd:function(){if(!this._map)return;this._mergeSplitClusters(),this._zoom=this._map._zoom,this._currentShownBounds=this._getExpandedVisibleBounds()},_moveEnd:function(){if(this._inZoomAnimation)return;var e=this._getExpandedVisibleBounds();this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,this._zoom,e),this._topClusterLevel._recursivelyAddChildrenToMap(null,this._zoom,e),this._currentShownBounds=e;return},_generateInitialClusters:function(){var e=this._map.getMaxZoom(),t=this.options.maxClusterRadius;this.options.disableClusteringAtZoom&&(e=this.options.disableClusteringAtZoom-1),this._maxZoom=e,this._gridClusters={},this._gridUnclustered={};for(var n=e;n>=0;n--)this._gridClusters[n]=new L.DistanceGrid(t),this._gridUnclustered[n]=new L.DistanceGrid(t);this._topClusterLevel=new L.MarkerCluster(this,-1)},_addLayer:function(e,t){var n=this._gridClusters,r=this._gridUnclustered,i,s;this.options.singleMarkerMode&&(e.options.icon=this.options.iconCreateFunction({getChildCount:function(){return 1},getAllChildMarkers:function(){return[e]}}));for(;t>=0;t--){i=this._map.project(e.getLatLng(),t);var o=n[t].getNearObject(i);if(o){o._addChild(e),e.__parent=o;return}o=r[t].getNearObject(i);if(o){var u=o.__parent;u&&this._removeLayer(o,!1);var a=new L.MarkerCluster(this,t,o,e);n[t].addObject(a,this._map.project(a._cLatLng,t)),o.__parent=a,e.__parent=a;var f=a;for(s=t-1;s>u._zoom;s--)f=new L.MarkerCluster(this,s,f),n[s].addObject(f,this._map.project(o.getLatLng(),s));u._addChild(f);for(s=t;s>=0;s--)if(!r[s].removeObject(o,this._map.project(o.getLatLng(),s)))break;return}r[t].addObject(e,i)}this._topClusterLevel._addChild(e),e.__parent=this._topClusterLevel;return},_mergeSplitClusters:function(){this._zoomthis._map._zoom?(this._animationStart(),this._animationZoomOut(this._zoom,this._map._zoom)):this._moveEnd()},_getExpandedVisibleBounds:function(){if(!this.options.removeOutsideVisibleBounds)return this.getBounds();var e=this._map,t=e.getBounds(),n=t._southWest,r=t._northEast,i=L.Browser.mobile?0:Math.abs(n.lat-r.lat),s=L.Browser.mobile?0:Math.abs(n.lng-r.lng);return new L.LatLngBounds(new L.LatLng(n.lat-i,n.lng-s,!0),new L.LatLng(r.lat+i,r.lng+s,!0))},_animationAddLayerNonAnimated:function(e,t){if(t===e)L.FeatureGroup.prototype.addLayer.call(this,e);else if(t._childCount===2){t._addToMap();var n=t.getAllChildMarkers();L.FeatureGroup.prototype.removeLayer.call(this,n[0]),L.FeatureGroup.prototype.removeLayer.call(this,n[1])}else t._updateIcon()}}),L.MarkerClusterGroup.include(L.DomUtil.TRANSITION?{_animationStart:function(){this._map._mapPane.className+=" leaflet-cluster-anim",this._inZoomAnimation++},_animationEnd:function(){this._map&&(this._map._mapPane.className=this._map._mapPane.className.replace(" leaflet-cluster-anim","")),this._inZoomAnimation--,this.fire("animationend")},_animationZoomIn:function(e,t){var n=this,r=this._getExpandedVisibleBounds(),i;this._topClusterLevel._recursively(r,e,0,function(s){var o=s._latlng,u=s._markers,a;s._isSingleParent()&&e+1===t?(L.FeatureGroup.prototype.removeLayer.call(n,s),s._recursivelyAddChildrenToMap(null,t,r)):(s.setOpacity(0),s._recursivelyAddChildrenToMap(o,t,r));for(i=u.length-1;i>=0;i--)a=u[i],r.contains(a._latlng)||L.FeatureGroup.prototype.removeLayer.call(n,a)}),this._forceLayout();var s,o;n._topClusterLevel._recursivelyBecomeVisible(r,t);for(s in n._layers)n._layers.hasOwnProperty(s)&&(o=n._layers[s],!(o instanceof L.MarkerCluster)&&o._icon&&o.setOpacity(1));n._topClusterLevel._recursively(r,e,t,function(e){e._recursivelyRestoreChildPositions(t)}),setTimeout(function(){n._topClusterLevel._recursively(r,e,0,function(e){L.FeatureGroup.prototype.removeLayer.call(n,e),e.setOpacity(1)}),n._animationEnd()},200)},_animationZoomOut:function(e,t){this._animationZoomOutSingle(this._topClusterLevel,e-1,t),this._topClusterLevel._recursivelyAddChildrenToMap(null,t,this._getExpandedVisibleBounds()),this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,e,this._getExpandedVisibleBounds())},_animationZoomOutSingle:function(e,t,n){var r=this._getExpandedVisibleBounds();e._recursivelyAnimateChildrenInAndAddSelfToMap(r,t+1,n);var i=this;this._forceLayout(),e._recursivelyBecomeVisible(r,n),setTimeout(function(){if(e._childCount===1){var s=e._markers[0];s.setLatLng(s.getLatLng()),s.setOpacity(1);return}e._recursively(r,n,0,function(e){e._recursivelyRemoveChildrenFromMap(r,t+1)}),i._animationEnd()},200)},_animationAddLayer:function(e,t){var n=this;L.FeatureGroup.prototype.addLayer.call(this,e),t!==e&&(t._childCount>2?(t._updateIcon(),this._forceLayout(),this._animationStart(),e._setPos(this._map.latLngToLayerPoint(t.getLatLng())),e.setOpacity(0),setTimeout(function(){L.FeatureGroup.prototype.removeLayer.call(n,e),e.setOpacity(1),n._animationEnd()},200)):(this._forceLayout(),n._animationStart(),n._animationZoomOutSingle(t,this._map.getMaxZoom(),this._map.getZoom())))},_forceLayout:function(){L.Util.falseFn(document.body.offsetWidth)}}:{_animationStart:function(){},_animationZoomIn:function(e,t){this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,e),this._topClusterLevel._recursivelyAddChildrenToMap(null,t,this._getExpandedVisibleBounds())},_animationZoomOut:function(e,t){this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,e),this._topClusterLevel._recursivelyAddChildrenToMap(null,t,this._getExpandedVisibleBounds())},_animationAddLayer:function(e,t){this._animationAddLayerNonAnimated(e,t)}}),L.MarkerCluster=L.Marker.extend({initialize:function(e,t,n,r){L.Marker.prototype.initialize.call(this,n?n._cLatLng||n.getLatLng():new L.LatLng(0,0),{icon:this}),this._group=e,this._zoom=t,this._markers=[],this._childClusters=[],this._childCount=0,this._iconNeedsUpdate=!0,this._bounds=new L.LatLngBounds,n&&this._addChild(n),r&&this._addChild(r)},getAllChildMarkers:function(e){e=e||[];for(var t=this._childClusters.length-1;t>=0;t--)this._childClusters[t].getAllChildMarkers(e);for(var n=this._markers.length-1;n>=0;n--)e.push(this._markers[n]);return e},getChildCount:function(){return this._childCount},zoomToBounds:function(){this._group._map.fitBounds(this._bounds)},_updateIcon:function(){this._iconNeedsUpdate=!0,this._icon&&this.setIcon(this)},createIcon:function(){return this._iconNeedsUpdate&&(this._iconObj=this._group.options.iconCreateFunction(this),this._iconNeedsUpdate=!1),this._iconObj.createIcon()},createShadow:function(){return this._iconObj.createShadow()},_addChild:function(e,t){this._iconNeedsUpdate=!0,this._expandBounds(e),e instanceof L.MarkerCluster?(t||(this._childClusters.push(e),e.__parent=this),this._childCount+=e._childCount):(t||this._markers.push(e),this._childCount++),this.__parent&&this.__parent._addChild(e,!0)},_expandBounds:function(e){var t,n=e._wLatLng||e._latlng;e instanceof L.MarkerCluster?(this._bounds.extend(e._bounds),t=e._childCount):(this._bounds.extend(n),t=1),this._cLatLng||(this._cLatLng=e._cLatLng||n);var r=this._childCount+t;this._wLatLng?(this._wLatLng.lat=(n.lat*t+this._wLatLng.lat*this._childCount)/r,this._wLatLng.lng=(n.lng*t+this._wLatLng.lng*this._childCount)/r):this._latlng=this._wLatLng=new L.LatLng(n.lat,n.lng)},_addToMap:function(e){e&&(this._backupLatlng=this._latlng,this.setLatLng(e)),L.FeatureGroup.prototype.addLayer.call(this._group,this)},_recursivelyAnimateChildrenIn:function(e,t,n){this._recursively(e,0,n-1,function(e){var n=e._markers,r,i;for(r=n.length-1;r>=0;r--)i=n[r],i._icon&&(i._setPos(t),i.setOpacity(0))},function(e){var n=e._childClusters,r,i;for(r=n.length-1;r>=0;r--)i=n[r],i._icon&&(i._setPos(t),i.setOpacity(0))})},_recursivelyAnimateChildrenInAndAddSelfToMap:function(e,t,n){this._recursively(e,n,0,function(r){r._recursivelyAnimateChildrenIn(e,r._group._map.latLngToLayerPoint(r.getLatLng()).round(),t),r._isSingleParent()&&t-1===n?(r.setOpacity(1),r._recursivelyRemoveChildrenFromMap(e,t)):r.setOpacity(0),r._addToMap()})},_recursivelyBecomeVisible:function(e,t){this._recursively(e,0,t,null,function(e){e.setOpacity(1)})},_recursivelyAddChildrenToMap:function(e,t,n){this._recursively(n,-1,t,function(r){if(t===r._zoom)return;for(var i=r._markers.length-1;i>=0;i--){var s=r._markers[i];if(!n.contains(s._latlng))continue;e&&(s._backupLatlng=s.getLatLng(),s.setLatLng(e),s.setOpacity(0)),L.FeatureGroup.prototype.addLayer.call(r._group,s)}},function(t){t._addToMap(e)})},_recursivelyRestoreChildPositions:function(e){for(var t=this._markers.length-1;t>=0;t--){var n=this._markers[t];n._backupLatlng&&(n.setLatLng(n._backupLatlng),delete n._backupLatlng)}if(e-1===this._zoom)for(var r=this._childClusters.length-1;r>=0;r--)this._childClusters[r]._restorePosition();else for(var i=this._childClusters.length-1;i>=0;i--)this._childClusters[i]._recursivelyRestoreChildPositions(e)},_restorePosition:function(){this._backupLatlng&&(this.setLatLng(this._backupLatlng),delete this._backupLatlng)},_recursivelyRemoveChildrenFromMap:function(e,t,n){var r,i;this._recursively(e,-1,t-1,function(e){for(i=e._markers.length-1;i>=0;i--){r=e._markers[i];if(!n||!n.contains(r._latlng))L.FeatureGroup.prototype.removeLayer.call(e._group,r),r.setOpacity(1)}},function(e){for(i=e._childClusters.length-1;i>=0;i--){r=e._childClusters[i];if(!n||!n.contains(r._latlng))L.FeatureGroup.prototype.removeLayer.call(e._group,r),r.setOpacity(1)}})},_recursively:function(e,t,n,r,i){var s=this._childClusters,o=this._zoom,u,a;if(t>o)for(u=s.length-1;u>=0;u--)a=s[u],e.intersects(a._bounds)&&a._recursively(e,t,n,r,i);else{r&&r(this),i&&this._zoom===n&&i(this);if(n>o)for(u=s.length-1;u>=0;u--)a=s[u],e.intersects(a._bounds)&&a._recursively(e,t,n,r,i)}},_recalculateBounds:function(){var e=this._markers,t=this._childClusters,n;this._bounds=new L.LatLngBounds,delete this._wLatLng;for(n=e.length-1;n>=0;n--)this._expandBounds(e[n]);for(n=t.length-1;n>=0;n--)this._expandBounds(t[n])},_isSingleParent:function(){return this._childClusters.length>0&&this._childClusters[0]._childCount===this._childCount}}),L.DistanceGrid=function(e){this._cellSize=e,this._sqCellSize=e*e,this._grid={},this._objectPoint={}},L.DistanceGrid.prototype={addObject:function(e,t){var n=this._getCoord(t.x),r=this._getCoord(t.y),i=this._grid,s=i[r]=i[r]||{},o=s[n]=s[n]||[],u=L.Util.stamp(e);this._objectPoint[u]=t,o.push(e)},updateObject:function(e,t){this.removeObject(e),this.addObject(e,t)},removeObject:function(e,t){var n=this._getCoord(t.x),r=this._getCoord(t.y),i=this._grid,s=i[r]=i[r]||{},o=s[n]=s[n]||[],u,a;delete this._objectPoint[L.Util.stamp(e)];for(u=0,a=o.length;u=0;s--){o=t[s],u=this.getDistant(o,e);if(!(u>0))continue;i.push(o),u>n&&(n=u,r=o)}return{maxPoint:r,newPoints:i}},buildConvexHull:function(e,t){var n=[],r=this.findMostDistantPointFromBaseLine(e,t);return r.maxPoint?(n=n.concat(this.buildConvexHull([e[0],r.maxPoint],r.newPoints)),n=n.concat(this.buildConvexHull([r.maxPoint,e[1]],r.newPoints)),n):[e]},getConvexHull:function(e){var t=!1,n=!1,r=null,i=null,s;for(s=e.length-1;s>=0;s--){var o=e[s];if(t===!1||o.lat>t)r=o,t=o.lat;if(n===!1||o.lat=0;s--)i=e[s].getLatLng(),t.push(i);r=L.QuickHull.getConvexHull(t);for(s=r.length-1;s>=0;s--)n.push(r[s][0]);return n}}),L.MarkerCluster.include({_2PI:Math.PI*2,_circleFootSeparation:25,_circleStartAngle:Math.PI/6,_spiralFootSeparation:28,_spiralLengthStart:11,_spiralLengthFactor:5,_circleSpiralSwitchover:9,spiderfy:function(){if(this._group._spiderfied===this||this._group._inZoomAnimation)return;var e=this.getAllChildMarkers(),t=this._group,n=t._map,r=n.latLngToLayerPoint(this._latlng),i;this._group._unspiderfy(),this._group._spiderfied=this,e.length>=this._circleSpiralSwitchover?i=this._generatePointsSpiral(e.length,r):(r.y+=10,i=this._generatePointsCircle(e.length,r)),this._animationSpiderfy(e,i)},unspiderfy:function(e){if(this._group._inZoomAnimation)return;this._animationUnspiderfy(e),this._group._spiderfied=null},_generatePointsCircle:function(e,t){var n=this._group.options.spiderfyDistanceMultiplier*this._circleFootSeparation*(2+e),r=n/this._2PI,i=this._2PI/e,s=[],o,u;s.length=e;for(o=e-1;o>=0;o--)u=this._circleStartAngle+o*i,s[o]=(new L.Point(t.x+r*Math.cos(u),t.y+r*Math.sin(u)))._round();return s},_generatePointsSpiral:function(e,t){var n=this._group.options.spiderfyDistanceMultiplier*this._spiralLengthStart,r=this._group.options.spiderfyDistanceMultiplier*this._spiralFootSeparation,i=this._group.options.spiderfyDistanceMultiplier*this._spiralLengthFactor,s=0,o=[],u;o.length=e;for(u=e-1;u>=0;u--)s+=r/n+u*5e-4,o[u]=(new L.Point(t.x+n*Math.cos(s),t.y+n*Math.sin(s)))._round(),n+=this._2PI*i/s;return o}}),L.MarkerCluster.include(L.DomUtil.TRANSITION?{SVG_ANIMATION:function(){return(document.createElementNS("http://www.w3.org/2000/svg","animate")+"").indexOf("SVGAnimate")>-1}(),_animationSpiderfy:function(e,t){var n=this,r=this._group,i=r._map,s=i.latLngToLayerPoint(this._latlng),o,u,a,f;for(o=e.length-1;o>=0;o--)u=e[o],u.setZIndexOffset(1e6),u.setOpacity(0),L.FeatureGroup.prototype.addLayer.call(r,u),u._setPos(s);r._forceLayout(),r._animationStart();var l=L.Path.SVG?0:.3,c=L.Path.SVG_NS;for(o=e.length-1;o>=0;o--){f=i.layerPointToLatLng(t[o]),u=e[o],u._preSpiderfyLatlng=u._latlng,u.setLatLng(f),u.setOpacity(1),a=new L.Polyline([n._latlng,f],{weight:1.5,color:"#222",opacity:l}),i.addLayer(a),u._spiderLeg=a;if(!L.Path.SVG||!this.SVG_ANIMATION)continue;var h=a._path.getTotalLength();a._path.setAttribute("stroke-dasharray",h+","+h);var p=document.createElementNS(c,"animate");p.setAttribute("attributeName","stroke-dashoffset"),p.setAttribute("begin","indefinite"),p.setAttribute("from",h),p.setAttribute("to",0),p.setAttribute("dur",.25),a._path.appendChild(p),p.beginElement(),p=document.createElementNS(c,"animate"),p.setAttribute("attributeName","stroke-opacity"),p.setAttribute("attributeName","stroke-opacity"),p.setAttribute("begin","indefinite"),p.setAttribute("from",0),p.setAttribute("to",.5),p.setAttribute("dur",.25),a._path.appendChild(p),p.beginElement()}n.setOpacity(.3);if(L.Path.SVG){this._group._forceLayout();for(o=e.length-1;o>=0;o--)u=e[o]._spiderLeg,u.options.opacity=.5,u._path.setAttribute("stroke-opacity",.5)}setTimeout(function(){r._animationEnd(),r.fire("spiderfied")},200)},_animationUnspiderfy:function(e){var t=this._group,n=t._map,r=e?n._latLngToNewLayerPoint(this._latlng,e.zoom,e.center):n.latLngToLayerPoint(this._latlng),i=this.getAllChildMarkers(),s=L.Path.SVG&&this.SVG_ANIMATION,o,u,a;t._animationStart(),this.setOpacity(1);for(u=i.length-1;u>=0;u--){o=i[u];if(!o._preSpiderfyLatlng)continue;o.setLatLng(o._preSpiderfyLatlng),delete o._preSpiderfyLatlng,o._setPos(r),o.setOpacity(0),s&&(a=o._spiderLeg._path.childNodes[0],a.setAttribute("to",a.getAttribute("from")),a.setAttribute("from",0),a.beginElement(),a=o._spiderLeg._path.childNodes[1],a.setAttribute("from",.5),a.setAttribute("to",0),a.setAttribute("stroke-opacity",0),a.beginElement(),o._spiderLeg._path.setAttribute("stroke-opacity",0))}setTimeout(function(){var e=0;for(u=i.length-1;u>=0;u--)o=i[u],o._spiderLeg&&e++;for(u=i.length-1;u>=0;u--){o=i[u];if(!o._spiderLeg)continue;o.setOpacity(1),o.setZIndexOffset(0),e>1&&L.FeatureGroup.prototype.removeLayer.call(t,o),n.removeLayer(o._spiderLeg),delete o._spiderLeg}t._animationEnd()},200)}}:{_animationSpiderfy:function(e,t){var n=this._group,r=n._map,i,s,o,u;for(i=e.length-1;i>=0;i--)u=r.layerPointToLatLng(t[i]),s=e[i],s._preSpiderfyLatlng=s._latlng,s.setLatLng(u),s.setZIndexOffset(1e6),L.FeatureGroup.prototype.addLayer.call(n,s),o=new L.Polyline([this._latlng,u],{weight:1.5,color:"#222"}),r.addLayer(o),s._spiderLeg=o;this.setOpacity(.3),n.fire("spiderfied")},_animationUnspiderfy:function(){var e=this._group,t=e._map,n=this.getAllChildMarkers(),r,i;this.setOpacity(1);for(i=n.length-1;i>=0;i--)r=n[i],L.FeatureGroup.prototype.removeLayer.call(e,r),r.setLatLng(r._preSpiderfyLatlng),delete r._preSpiderfyLatlng,r.setZIndexOffset(0),t.removeLayer(r._spiderLeg),delete r._spiderLeg}}),L.MarkerClusterGroup.include({_spiderfied:null,_spiderfierOnAdd:function(){this._map.on("click",this._unspiderfyWrapper,this),this._map.options.zoomAnimation?this._map.on("zoomstart",this._unspiderfyZoomStart,this):this._map.on("zoomend",this._unspiderfyWrapper,this),L.Path.SVG&&!L.Browser.touch&&this._map._initPathRoot()},_spiderfierOnRemove:function(){this._map.off("click",this._unspiderfyWrapper,this),this._map.off("zoomstart",this._unspiderfyZoomStart,this),this._map.off("zoomanim",this._unspiderfyZoomAnim,this),this._unspiderfy()},_unspiderfyZoomStart:function(){if(!this._map)return;this._map.on("zoomanim",this._unspiderfyZoomAnim,this)},_unspiderfyZoomAnim:function(e){if(L.DomUtil.hasClass(this._map._mapPane,"leaflet-touching"))return;this._map.off("zoomanim",this._unspiderfyZoomAnim,this),this._unspiderfy(e)},_unspiderfyWrapper:function(){this._unspiderfy()},_unspiderfy:function(e){this._spiderfied&&this._spiderfied.unspiderfy(e)},_unspiderfyLayer:function(e){e._spiderLeg&&(L.FeatureGroup.prototype.removeLayer.call(this,e),e.setOpacity(1),e.setZIndexOffset(0),this._map.removeLayer(e._spiderLeg),delete e._spiderLeg)}})})(this);
\ No newline at end of file
diff --git a/chimere/templates/chimere/blocks/map.html b/chimere/templates/chimere/blocks/map.html
index 6d4508d..2614f39 100644
--- a/chimere/templates/chimere/blocks/map.html
+++ b/chimere/templates/chimere/blocks/map.html
@@ -50,6 +50,7 @@
{% if restricted_extent %}{{ restricted_extent }}
chimere_init_options["restricted_extent"] = bounds;
{% endif %}
+ {% if enable_clustering %}chimere_init_options['enable_clustering'] = true;{% endif%}
{% if selected_map_layer %}
chimere_init_options["selected_map_layer"] = {{selected_map_layer}};
{% endif %}
diff --git a/chimere/templatetags/chimere_tags.py b/chimere/templatetags/chimere_tags.py
index 47f0800..f180061 100644
--- a/chimere/templatetags/chimere_tags.py
+++ b/chimere/templatetags/chimere_tags.py
@@ -190,6 +190,10 @@ def map(context, map_id='map'):
context_data['icon_offset_y'] = settings.CHIMERE_ICON_OFFSET_Y
context_data['icon_width'] = settings.CHIMERE_ICON_WIDTH
context_data['icon_height'] = settings.CHIMERE_ICON_HEIGHT
+ context_data['enable_clustering'] = True \
+ if hasattr(settings, 'CHIMERE_ENABLE_CLUSTERING') and \
+ settings.CHIMERE_ENABLE_CLUSTERING \
+ else False
context_data['routing'] = 'true' \
if hasattr(settings, 'CHIMERE_ENABLE_ROUTING') and \
settings.CHIMERE_ENABLE_ROUTING \
--
cgit v1.2.3
From 54facb137f45e9ea5fc774203c45d508184973d5 Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Sun, 7 Apr 2013 21:49:04 +0200
Subject: Leaflet: first fully functionnal version of clustering
---
chimere/settings.sample.py | 20 ++----
chimere/static/chimere/css/styles.css | 51 ++++++++++++++
.../static/chimere/img/marker-cluster-large.png | Bin 0 -> 1182 bytes
.../static/chimere/img/marker-cluster-medium.png | Bin 0 -> 861 bytes
.../static/chimere/img/marker-cluster-small.png | Bin 0 -> 701 bytes
.../static/chimere/js/jquery.chimere-leaflet.js | 75 ++++++++++++++++++---
6 files changed, 124 insertions(+), 22 deletions(-)
create mode 100644 chimere/static/chimere/img/marker-cluster-large.png
create mode 100644 chimere/static/chimere/img/marker-cluster-medium.png
create mode 100644 chimere/static/chimere/img/marker-cluster-small.png
diff --git a/chimere/settings.sample.py b/chimere/settings.sample.py
index b23f31e..22502b2 100644
--- a/chimere/settings.sample.py
+++ b/chimere/settings.sample.py
@@ -307,27 +307,21 @@ if 'MAP_CSS_URLS' not in globals():
global MAP_CSS_URLS
MAP_CSS_URLS = {
'openlayers':["http://www.openlayers.org/api/theme/default/style.css"],
- 'leaflet':[STATIC_URL + "leaflet/leaflet.css"],
+ 'leaflet':[STATIC_URL + "leaflet/leaflet.css",
+ STATIC_URL + "leaflet-markercluster/MarkerCluster.css",
+ STATIC_URL + "leaflet-markercluster/MarkerCluster.Default.css",
+ ]
}
- if CHIMERE_ENABLE_CLUSTERING:
- MAP_CSS_URLS['leaflet'] += [
- STATIC_URL + "leaflet-markercluster/MarkerCluster.css",
- STATIC_URL + "leaflet-markercluster/MarkerCluster.Default.css",
- ]
# key: [(url, condition)]
MAP_CONDITIONNAL_CSS_URLS = {
'openlayers':[("http://www.openlayers.org/api/theme/default/style.css",
None)],
'leaflet':[
- (STATIC_URL + "leaflet/leaflet.css", None),
- (STATIC_URL + "leaflet/leaflet.ie.css", 'lte IE 8')
- ]
- }
- if CHIMERE_ENABLE_CLUSTERING:
- MAP_CSS_URLS['leaflet'] += [
+ (STATIC_URL + "leaflet/leaflet.css", None),
+ (STATIC_URL + "leaflet/leaflet.ie.css", 'lte IE 8')
(STATIC_URL + "leaflet-markercluster/MarkerCluster.css", None),
(STATIC_URL + "leaflet-markercluster/MarkerCluster.Default.css", None),
(STATIC_URL + "leaflet-markercluster/MarkerCluster.Default.ie.css",
'lte IE 8'),
]
-
+ }
diff --git a/chimere/static/chimere/css/styles.css b/chimere/static/chimere/css/styles.css
index 764e0c7..a123a5d 100644
--- a/chimere/static/chimere/css/styles.css
+++ b/chimere/static/chimere/css/styles.css
@@ -1109,3 +1109,54 @@ div.pp_default .pp_expand{
left:6px;
bottom:5px;
}
+
+.marker-cluster.marker-cluster-large {
+ border-radius: 25px;
+}
+
+.marker-cluster.marker-cluster-small {
+ border-radius: 15px;
+}
+
+.marker-cluster.marker-cluster-large div {
+ width: 40px;
+ height: 40px;
+ border-radius: 20px;
+}
+
+.marker-cluster.marker-cluster-small div {
+ width: 20px;
+ height: 20px;
+ border-radius: 10px;
+}
+
+.marker-cluster.marker-cluster-large span {
+ line-height: 40px;
+}
+
+.marker-cluster.marker-cluster-small span {
+ line-height: 20px;
+}
+
+.marker-cluster-small-number,
+.marker-cluster-medium-number,
+.marker-cluster-large-number{
+ position: relative;
+ font: 12px "Helvetica Neue", Arial, Helvetica, sans-serif;
+ text-align: center;
+}
+
+.marker-cluster-small-number{
+ width: 30px;
+ top: -22px;
+}
+
+.marker-cluster-medium-number{
+ width: 40px;
+ top: -27px;
+}
+
+.marker-cluster-large-number{
+ width: 50px;
+ top: -32px;
+}
diff --git a/chimere/static/chimere/img/marker-cluster-large.png b/chimere/static/chimere/img/marker-cluster-large.png
new file mode 100644
index 0000000..477e2b8
Binary files /dev/null and b/chimere/static/chimere/img/marker-cluster-large.png differ
diff --git a/chimere/static/chimere/img/marker-cluster-medium.png b/chimere/static/chimere/img/marker-cluster-medium.png
new file mode 100644
index 0000000..1d9099b
Binary files /dev/null and b/chimere/static/chimere/img/marker-cluster-medium.png differ
diff --git a/chimere/static/chimere/img/marker-cluster-small.png b/chimere/static/chimere/img/marker-cluster-small.png
new file mode 100644
index 0000000..ae5ce5a
Binary files /dev/null and b/chimere/static/chimere/img/marker-cluster-small.png differ
diff --git a/chimere/static/chimere/js/jquery.chimere-leaflet.js b/chimere/static/chimere/js/jquery.chimere-leaflet.js
index 36729c1..ef156fb 100644
--- a/chimere/static/chimere/js/jquery.chimere-leaflet.js
+++ b/chimere/static/chimere/js/jquery.chimere-leaflet.js
@@ -95,7 +95,12 @@ See the file COPYING for details.
marker_hover_offset: null,
icon_start: null,
icon_step: null,
- icon_end: null
+ icon_end: null,
+ weight_steps: new Array(6, 15),
+ weight_icon_sizes: new Array(30, 40, 50),
+ weight_icon_classes: new Array('marker-cluster-small',
+ 'marker-cluster-medium',
+ 'marker-cluster-large'),
};
var settings = {};
/*
@@ -121,19 +126,54 @@ See the file COPYING for details.
}
settings.icons = new Object();
if (settings.enable_clustering){
+ settings.NumberedDivIcon = L.Icon.extend({
+ options: {
+ iconUrl: STATIC_URL + 'chimere/img/empty.png',
+ number: '',
+ shadowUrl: null,
+ iconSize: new L.Point(40, 40),
+ iconAnchor: new L.Point(20, 20),
+ popupAnchor: new L.Point(0, -20),
+ className: '',
+ numberClassName: settings.weight_icon_classes[0]+'-number',
+ },
+
+ createIcon: function () {
+ var div = document.createElement('div');
+ var img = this._createImg(this.options['iconUrl']);
+ var numdiv = document.createElement('div');
+ numdiv.setAttribute( "class", this.options['numberClassName']);
+ numdiv.innerHTML = this.options['number'] || '';
+ div.appendChild ( img );
+ div.appendChild ( numdiv );
+ this._setIconStyles(div, 'icon');
+ return div;
+ },
+ createShadow: function () {
+ return null;
+ }
+ });
+
settings.layerMarkers = new L.MarkerClusterGroup({
- iconCreateFunction: function (cluster) {
+ iconCreateFunction : function (cluster) {
var markers = cluster.getAllChildMarkers();
var weight = 0;
for (idx=0;idx' + weight + ' ',
+ className: 'marker-cluster ' + settings.weight_icon_classes[idx],
+ iconSize: new L.Point(settings.weight_icon_sizes[idx],
+ settings.weight_icon_sizes[idx])
+ });
+ }
});
} else {
settings.layerMarkers = L.geoJson(null, {
@@ -355,12 +395,29 @@ See the file COPYING for details.
if (feature.properties.weight){
weight = feature.properties.weight;
}
+ var idx = 2;
+ if (weight < settings.weight_steps[0]) {
+ idx = 0;
+ } else if (weight < settings.weight_steps[1]) {
+ idx = 1;
+ }
+ var size = settings.weight_icon_sizes[idx];
+ var icon = new settings.NumberedDivIcon({
+ number: weight,
+ iconSize: new L.Point(size, size),
+ iconAnchor: new L.Point(size/2, size/2),
+ popupAnchor: new L.Point(0, -size/2),
+ iconUrl:STATIC_URL + "chimere/img/" + settings.weight_icon_classes[idx] + ".png",
+ numberClassName:settings.weight_icon_classes[idx] + "-number"
+ });
+;
var marker = new L.Marker(
new L.LatLng(
feature.geometry.coordinates[1],
feature.geometry.coordinates[0]),
{ title: feature.properties.name,
- weight: weight});
+ weight: weight,
+ icon:icon});
marker.bindPopup(feature.properties.name);
settings.layerMarkers.addLayer(marker);
}
--
cgit v1.2.3
From 04e654a925565f75a9efdb845de88d166bfbd76e Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Tue, 9 Apr 2013 01:58:59 +0200
Subject: Improve JSON generation performance
---
chimere/models.py | 20 ++++++++++++++++++++
chimere/views.py | 33 ++++++++++++++++++++++++++++-----
2 files changed, 48 insertions(+), 5 deletions(-)
diff --git a/chimere/models.py b/chimere/models.py
index c269439..7db530a 100644
--- a/chimere/models.py
+++ b/chimere/models.py
@@ -624,6 +624,19 @@ class Marker(GeographicItem):
val = values[unicode(propertymodel.id)]
self.setProperty(propertymodel, val)
+ def _getItems(self, base_dct={"properties":{}}):
+ '''Return a dict representation for json
+ '''
+ item = base_dct
+ item["geometry"] = {"type": "Point",
+ "coordinates": [ self.point.x, self.point.y ]
+ }
+ item["properties"]['pk'] = self.pk
+ item["properties"]['name'] = self.name
+ if self.weight:
+ item["properties"]['weight'] = self.weight
+ return item
+
def getGeoJSON(self, categories_id=[]):
'''Return a GeoJSON string
'''
@@ -1117,6 +1130,13 @@ class Route(GeographicItem):
properties.append(property)
return properties
+ def _getItems(self, dct={'properties':{}}):
+ dct['geometry'] = { "type": "LineString",
+ "coordinates": [[point.x, point.y]
+ for point in self.route]}
+ dct['properties'].update({'id':self.id, 'name':self.name})
+ return dct
+
def getGeoJSON(self, color="#000"):
'''Return a GeoJSON string
'''
diff --git a/chimere/views.py b/chimere/views.py
index 7c02216..79d4ac2 100644
--- a/chimere/views.py
+++ b/chimere/views.py
@@ -604,10 +604,17 @@ def getGeoObjects(request, area_name, category_ids, status):
idx = 0
current_cat = c_cat
colors = list(Color.objects.filter(color_theme = c_cat.color_theme))
+ color = '000'
if colors:
- jsons.append(route.getGeoJSON(color=colors[idx % len(colors)].code))
- else:
- jsons.append(route.getGeoJSON(color='000'))
+ color = colors[idx % len(colors)].code
+ if '#' not in color:
+ color = '#' + color
+ base_dct = {"type":"Feature",
+ "properties":{
+ "color":color
+ }
+ }
+ jsons.append(route._getItems(base_dct))
idx += 1
try:
q = checkDate(Q(status__in=status, categories__in=category_ids))
@@ -615,10 +622,26 @@ def getGeoObjects(request, area_name, category_ids, status):
except:
return HttpResponse('no results')
category_ids = [int(cat_id) for cat_id in category_ids]
- jsons += [geo_object.getGeoJSON(category_ids) for geo_object in list(query)]
+ for category_id in category_ids:
+ cat = SubCategory.objects.get(pk=category_id)
+ base_dct = {"type":"Feature",
+ "properties":{
+ "icon_path":unicode(cat.icon.image),
+ "icon_hover_path":cat.hover_icon.image \
+ if cat.hover_icon else '',
+ "icon_width":cat.icon.image.width,
+ 'icon_height':cat.icon.image.height,
+ 'category_name':cat.name}
+ }
+ for obj in query.filter(categories__pk=category_id).all():
+ dct = base_dct.copy()
+ dct['properties'] = base_dct['properties'].copy()
+ jsons.append(obj._getItems(base_dct=dct))
if not jsons:
return HttpResponse('no results')
- data = '{"type": "FeatureCollection", "features":[%s]}' % ",".join(jsons)
+ data = {"type": "FeatureCollection", "features":jsons}
+ data = json.dumps(data)
+
return HttpResponse(data)
def get_all_categories(request, area_name=None):
--
cgit v1.2.3
From c0c80a3e921fd69b6fa9950356fb7c12a1b15df4 Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Tue, 9 Apr 2013 01:59:20 +0200
Subject: Improve leaflet performance
---
chimere/static/chimere/js/jquery.chimere-leaflet.js | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/chimere/static/chimere/js/jquery.chimere-leaflet.js b/chimere/static/chimere/js/jquery.chimere-leaflet.js
index ef156fb..5cbeaac 100644
--- a/chimere/static/chimere/js/jquery.chimere-leaflet.js
+++ b/chimere/static/chimere/js/jquery.chimere-leaflet.js
@@ -155,6 +155,8 @@ See the file COPYING for details.
});
settings.layerMarkers = new L.MarkerClusterGroup({
+ spiderfyOnMaxZoom: false, showCoverageOnHover: false,
+ maxClusterRadius:50,
iconCreateFunction : function (cluster) {
var markers = cluster.getAllChildMarkers();
var weight = 0;
@@ -385,6 +387,8 @@ See the file COPYING for details.
$.ajax({url: uri,
dataType: "json",
success: function (data) {
+ settings.map.removeLayer(settings.layerMarkers);
+ settings.map.removeLayer(settings.layerVectors);
settings.layerMarkers.clearLayers();
settings.layerVectors.clearLayers();
if (settings.enable_clustering){
@@ -410,7 +414,6 @@ See the file COPYING for details.
iconUrl:STATIC_URL + "chimere/img/" + settings.weight_icon_classes[idx] + ".png",
numberClassName:settings.weight_icon_classes[idx] + "-number"
});
-;
var marker = new L.Marker(
new L.LatLng(
feature.geometry.coordinates[1],
@@ -430,6 +433,8 @@ See the file COPYING for details.
} else {
settings.layerMarkers.addData(data);
}
+ settings.map.addLayer(settings.layerMarkers);
+ settings.map.addLayer(settings.layerVectors);
},
error: function (data, textStatus, errorThrown) {
settings.layerMarkers.clearLayers();
--
cgit v1.2.3
From aaa90f1194646386344b1d5ca2f83a609beee17f Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Thu, 11 Apr 2013 12:10:57 +0200
Subject: Manage object with not category in getGeoObjects
---
chimere/views.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/chimere/views.py b/chimere/views.py
index 79d4ac2..136f34a 100644
--- a/chimere/views.py
+++ b/chimere/views.py
@@ -623,6 +623,8 @@ def getGeoObjects(request, area_name, category_ids, status):
return HttpResponse('no results')
category_ids = [int(cat_id) for cat_id in category_ids]
for category_id in category_ids:
+ if not category_id:
+ continue
cat = SubCategory.objects.get(pk=category_id)
base_dct = {"type":"Feature",
"properties":{
--
cgit v1.2.3
From 5cb7e1f3ce32a18b868453ccc6f3fb7fea04b876 Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Mon, 22 Apr 2013 22:51:34 +0200
Subject: Add more cluster levels
---
chimere/src/marker-cluster-large.xcf | Bin 0 -> 4179 bytes
chimere/src/marker-cluster-medium.xcf | Bin 0 -> 2883 bytes
chimere/src/marker-cluster-small.xcf | Bin 0 -> 2423 bytes
chimere/src/marker-cluster-xlarge.xcf | Bin 0 -> 3125 bytes
chimere/src/marker-cluster-xsmall.xcf | Bin 0 -> 2407 bytes
chimere/src/marker-cluster-xxlarge.xcf | Bin 0 -> 4257 bytes
chimere/src/share-icon.xcf | Bin 0 -> 5431 bytes
chimere/static/chimere/css/styles.css | 120 +++++++++++++++++++--
.../static/chimere/img/marker-cluster-large.png | Bin 1182 -> 1115 bytes
.../static/chimere/img/marker-cluster-small.png | Bin 701 -> 2067 bytes
.../static/chimere/img/marker-cluster-xlarge.png | Bin 0 -> 1182 bytes
.../static/chimere/img/marker-cluster-xsmall.png | Bin 0 -> 693 bytes
.../static/chimere/img/marker-cluster-xxlarge.png | Bin 0 -> 1364 bytes
chimere/static/chimere/img/share-icon.xcf | Bin 5431 -> 0 bytes
.../static/chimere/js/jquery.chimere-leaflet.js | 12 ++-
15 files changed, 118 insertions(+), 14 deletions(-)
create mode 100755 chimere/src/marker-cluster-large.xcf
create mode 100755 chimere/src/marker-cluster-medium.xcf
create mode 100755 chimere/src/marker-cluster-small.xcf
create mode 100755 chimere/src/marker-cluster-xlarge.xcf
create mode 100755 chimere/src/marker-cluster-xsmall.xcf
create mode 100755 chimere/src/marker-cluster-xxlarge.xcf
create mode 100755 chimere/src/share-icon.xcf
create mode 100644 chimere/static/chimere/img/marker-cluster-xlarge.png
create mode 100644 chimere/static/chimere/img/marker-cluster-xsmall.png
create mode 100644 chimere/static/chimere/img/marker-cluster-xxlarge.png
delete mode 100644 chimere/static/chimere/img/share-icon.xcf
diff --git a/chimere/src/marker-cluster-large.xcf b/chimere/src/marker-cluster-large.xcf
new file mode 100755
index 0000000..ac0e14d
Binary files /dev/null and b/chimere/src/marker-cluster-large.xcf differ
diff --git a/chimere/src/marker-cluster-medium.xcf b/chimere/src/marker-cluster-medium.xcf
new file mode 100755
index 0000000..85de110
Binary files /dev/null and b/chimere/src/marker-cluster-medium.xcf differ
diff --git a/chimere/src/marker-cluster-small.xcf b/chimere/src/marker-cluster-small.xcf
new file mode 100755
index 0000000..3e38c15
Binary files /dev/null and b/chimere/src/marker-cluster-small.xcf differ
diff --git a/chimere/src/marker-cluster-xlarge.xcf b/chimere/src/marker-cluster-xlarge.xcf
new file mode 100755
index 0000000..6c9c890
Binary files /dev/null and b/chimere/src/marker-cluster-xlarge.xcf differ
diff --git a/chimere/src/marker-cluster-xsmall.xcf b/chimere/src/marker-cluster-xsmall.xcf
new file mode 100755
index 0000000..f0787cc
Binary files /dev/null and b/chimere/src/marker-cluster-xsmall.xcf differ
diff --git a/chimere/src/marker-cluster-xxlarge.xcf b/chimere/src/marker-cluster-xxlarge.xcf
new file mode 100755
index 0000000..4c9be7c
Binary files /dev/null and b/chimere/src/marker-cluster-xxlarge.xcf differ
diff --git a/chimere/src/share-icon.xcf b/chimere/src/share-icon.xcf
new file mode 100755
index 0000000..d149768
Binary files /dev/null and b/chimere/src/share-icon.xcf differ
diff --git a/chimere/static/chimere/css/styles.css b/chimere/static/chimere/css/styles.css
index a123a5d..b9c9975 100644
--- a/chimere/static/chimere/css/styles.css
+++ b/chimere/static/chimere/css/styles.css
@@ -1110,45 +1110,110 @@ div.pp_default .pp_expand{
bottom:5px;
}
-.marker-cluster.marker-cluster-large {
+.marker-cluster.marker-cluster-xxlarge {
+ border-radius: 27px;
+}
+
+.marker-cluster.marker-cluster-xlarge {
border-radius: 25px;
}
+.marker-cluster.marker-cluster-large {
+ border-radius: 22px;
+}
+
+.marker-cluster.marker-cluster-medium {
+ border-radius: 20px;
+}
+
.marker-cluster.marker-cluster-small {
+ border-radius: 17px;
+}
+
+.marker-cluster.marker-cluster-xsmall {
border-radius: 15px;
}
-.marker-cluster.marker-cluster-large div {
+.marker-cluster.marker-cluster-xxlarge div {
+ width: 44px;
+ height: 44px;
+ border-radius: 22px;
+}
+
+.marker-cluster.marker-cluster-xlarge div {
width: 40px;
height: 40px;
border-radius: 20px;
}
+.marker-cluster.marker-cluster-large div {
+ width: 34px;
+ height: 34px;
+ border-radius: 17px;
+}
+
+.marker-cluster.marker-cluster-medium div {
+ width: 30px;
+ height: 30px;
+ border-radius: 15px;
+}
+
.marker-cluster.marker-cluster-small div {
+ width: 24px;
+ height: 24px;
+ border-radius: 12px;
+}
+
+.marker-cluster.marker-cluster-xsmall div {
width: 20px;
height: 20px;
border-radius: 10px;
}
-.marker-cluster.marker-cluster-large span {
+.marker-cluster.marker-cluster-xxlarge span {
+ line-height: 44px;
+}
+
+.marker-cluster.marker-cluster-xlarge span {
line-height: 40px;
}
+.marker-cluster.marker-cluster-large span {
+ line-height: 34px;
+}
+
.marker-cluster.marker-cluster-small span {
+ line-height: 24px;
+}
+
+.marker-cluster.marker-cluster-xsmall span {
line-height: 20px;
}
+.marker-cluster-xsmall-number,
.marker-cluster-small-number,
.marker-cluster-medium-number,
-.marker-cluster-large-number{
+.marker-cluster-large-number
+.marker-cluster-xlarge-number
+.marker-cluster-xxlarge-number{
position: relative;
font: 12px "Helvetica Neue", Arial, Helvetica, sans-serif;
text-align: center;
}
-.marker-cluster-small-number{
- width: 30px;
- top: -22px;
+.marker-cluster-xxlarge-number{
+ width: 55px;
+ top: -35px;
+}
+
+.marker-cluster-xlarge-number{
+ width: 50px;
+ top: -32px;
+}
+
+.marker-cluster-large-number{
+ width: 45px;
+ top: -30px;
}
.marker-cluster-medium-number{
@@ -1156,7 +1221,42 @@ div.pp_default .pp_expand{
top: -27px;
}
-.marker-cluster-large-number{
- width: 50px;
- top: -32px;
+.marker-cluster-small-number{
+ width: 35px;
+ top: -25px;
+}
+
+.marker-cluster-xsmall-number{
+ width: 30px;
+ top: -22px;
+}
+
+.marker-cluster-xsmall {
+ background-color: rgba(221, 255, 180, 0.6);
+}
+
+.marker-cluster-xsmall div {
+ background-color: rgba(150, 244, 97, 0.6);
+}
+
+.marker-cluster-large {
+ background-color: rgba(255, 214, 140, 0.6);
}
+.marker-cluster-large div {
+ background-color: rgba(255, 183, 54, 0.6);
+}
+
+.marker-cluster-xlarge {
+ background-color: rgba(253, 156, 115, 0.6);
+}
+.marker-cluster-xlarge div {
+ background-color: rgba(241, 128, 23, 0.6);
+}
+
+.marker-cluster-xxlarge {
+ background-color: rgba(255, 174, 176, 0.6);
+}
+.marker-cluster-xxlarge div {
+ background-color: rgba(255, 48, 54, 0.6);
+}
+
diff --git a/chimere/static/chimere/img/marker-cluster-large.png b/chimere/static/chimere/img/marker-cluster-large.png
index 477e2b8..eef6a73 100644
Binary files a/chimere/static/chimere/img/marker-cluster-large.png and b/chimere/static/chimere/img/marker-cluster-large.png differ
diff --git a/chimere/static/chimere/img/marker-cluster-small.png b/chimere/static/chimere/img/marker-cluster-small.png
index ae5ce5a..0620694 100644
Binary files a/chimere/static/chimere/img/marker-cluster-small.png and b/chimere/static/chimere/img/marker-cluster-small.png differ
diff --git a/chimere/static/chimere/img/marker-cluster-xlarge.png b/chimere/static/chimere/img/marker-cluster-xlarge.png
new file mode 100644
index 0000000..477e2b8
Binary files /dev/null and b/chimere/static/chimere/img/marker-cluster-xlarge.png differ
diff --git a/chimere/static/chimere/img/marker-cluster-xsmall.png b/chimere/static/chimere/img/marker-cluster-xsmall.png
new file mode 100644
index 0000000..748b3ed
Binary files /dev/null and b/chimere/static/chimere/img/marker-cluster-xsmall.png differ
diff --git a/chimere/static/chimere/img/marker-cluster-xxlarge.png b/chimere/static/chimere/img/marker-cluster-xxlarge.png
new file mode 100644
index 0000000..d7b435f
Binary files /dev/null and b/chimere/static/chimere/img/marker-cluster-xxlarge.png differ
diff --git a/chimere/static/chimere/img/share-icon.xcf b/chimere/static/chimere/img/share-icon.xcf
deleted file mode 100644
index d149768..0000000
Binary files a/chimere/static/chimere/img/share-icon.xcf and /dev/null differ
diff --git a/chimere/static/chimere/js/jquery.chimere-leaflet.js b/chimere/static/chimere/js/jquery.chimere-leaflet.js
index 5cbeaac..0b417e5 100644
--- a/chimere/static/chimere/js/jquery.chimere-leaflet.js
+++ b/chimere/static/chimere/js/jquery.chimere-leaflet.js
@@ -96,11 +96,15 @@ See the file COPYING for details.
icon_start: null,
icon_step: null,
icon_end: null,
- weight_steps: new Array(6, 15),
- weight_icon_sizes: new Array(30, 40, 50),
- weight_icon_classes: new Array('marker-cluster-small',
+ weight_steps: new Array(10, 50, 100, 500, 1000),
+ weight_icon_sizes: new Array(30, 34, 40, 44, 50, 54),
+ weight_icon_classes: new Array('marker-cluster-xsmall',
+ 'marker-cluster-small',
'marker-cluster-medium',
- 'marker-cluster-large'),
+ 'marker-cluster-large',
+ 'marker-cluster-xlarge',
+ 'marker-cluster-xxlarge'
+ )
};
var settings = {};
/*
--
cgit v1.2.3
From 2c22c60e8ac7670b8224b1e6c22dba7ff978351d Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Mon, 22 Apr 2013 23:10:14 +0200
Subject: Fix cluster management with several levels
---
chimere/static/chimere/js/jquery.chimere-leaflet.js | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/chimere/static/chimere/js/jquery.chimere-leaflet.js b/chimere/static/chimere/js/jquery.chimere-leaflet.js
index 0b417e5..56ba696 100644
--- a/chimere/static/chimere/js/jquery.chimere-leaflet.js
+++ b/chimere/static/chimere/js/jquery.chimere-leaflet.js
@@ -167,11 +167,12 @@ See the file COPYING for details.
for (idx=0;idx' + weight + ' ',
--
cgit v1.2.3
From 2bd78502b51ddd57e24a881b59811ac1863f58f2 Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Mon, 22 Apr 2013 23:58:44 +0200
Subject: Manage many layers with Leaflet
---
chimere/static/chimere/js/jquery.chimere-leaflet.js | 9 +++++++--
chimere/templates/chimere/blocks/map.html | 1 +
chimere/templatetags/chimere_tags.py | 2 ++
3 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/chimere/static/chimere/js/jquery.chimere-leaflet.js b/chimere/static/chimere/js/jquery.chimere-leaflet.js
index 56ba696..51bd00a 100644
--- a/chimere/static/chimere/js/jquery.chimere-leaflet.js
+++ b/chimere/static/chimere/js/jquery.chimere-leaflet.js
@@ -41,6 +41,7 @@ See the file COPYING for details.
permalink_div: null,
permalink: null, // OL Control, could be overrided
map_layers: null,
+ map_layer_names: null,
selected_map_layer: null,
dynamic_categories: false,
display_submited: false,
@@ -120,9 +121,13 @@ See the file COPYING for details.
var map_element = $(this).attr('id');
settings.map = map = L.map(map_element);
- for (idx in settings.map_layers){
- map.addLayer(settings.map_layers[idx]);
+ map.addLayer(settings.map_layers[0]);
+ var map_layers = {};
+ for (idx=0 ; idx < settings.map_layers.length ; idx++){
+ map_layers[settings.map_layer_names[idx]] = settings.map_layers[idx];
+ alert(map_layers[settings.map_layer_names[idx]]);
}
+ L.control.layers(map_layers).addTo(map);
if(settings.zoom && settings.lat && settings.lon){
map.setView([settings.lat, settings.lon], settings.zoom);
} else {
diff --git a/chimere/templates/chimere/blocks/map.html b/chimere/templates/chimere/blocks/map.html
index 2614f39..2c225de 100644
--- a/chimere/templates/chimere/blocks/map.html
+++ b/chimere/templates/chimere/blocks/map.html
@@ -21,6 +21,7 @@
var chimere_init_options = {};
chimere_init_options["default_icon"] = '{{STATIC_URL}}img/marker-green.png';
chimere_init_options["map_layers"] = [{{js_map_layers|safe|escape}}];
+ chimere_init_options["map_layer_names"] = [{{js_map_layer_names|safe|escape}}];
chimere_init_options['permalink_label'] = '{%trans "Permalink"%}';
chimere_init_options['permalink_element'] = document.getElementById('permalink');
chimere_init_options['routing'] = {{routing}};
diff --git a/chimere/templatetags/chimere_tags.py b/chimere/templatetags/chimere_tags.py
index f180061..fd700d9 100644
--- a/chimere/templatetags/chimere_tags.py
+++ b/chimere/templatetags/chimere_tags.py
@@ -202,6 +202,8 @@ def map(context, map_id='map'):
map_layers, default_area = get_map_layers(area_name)
context_data['js_map_layers'] = ", ".join(
[js for name, js, default in map_layers])
+ context_data['js_map_layer_names'] = '"'+ '", "'.join(
+ [name for name, js, default in map_layers]) + '"'
context_data['map_layers'] = map_layers
if default_area:
context_data['selected_map_layer'] = default_area
--
cgit v1.2.3
From bb5d66ee68c4119c78d7a054180cce71be359fe4 Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Tue, 23 Apr 2013 00:02:51 +0200
Subject: Remove debug
---
chimere/static/chimere/js/jquery.chimere-leaflet.js | 1 -
1 file changed, 1 deletion(-)
diff --git a/chimere/static/chimere/js/jquery.chimere-leaflet.js b/chimere/static/chimere/js/jquery.chimere-leaflet.js
index 51bd00a..b48a54e 100644
--- a/chimere/static/chimere/js/jquery.chimere-leaflet.js
+++ b/chimere/static/chimere/js/jquery.chimere-leaflet.js
@@ -125,7 +125,6 @@ See the file COPYING for details.
var map_layers = {};
for (idx=0 ; idx < settings.map_layers.length ; idx++){
map_layers[settings.map_layer_names[idx]] = settings.map_layers[idx];
- alert(map_layers[settings.map_layer_names[idx]]);
}
L.control.layers(map_layers).addTo(map);
if(settings.zoom && settings.lat && settings.lon){
--
cgit v1.2.3
From 2860fd0cf740783bfe666ba969c023ccbce9ef5e Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Tue, 23 Apr 2013 00:27:33 +0200
Subject: Allow generalization of CSV export
---
chimere/admin.py | 20 ++++++++++++--------
chimere/utils.py | 10 +++++++---
2 files changed, 19 insertions(+), 11 deletions(-)
diff --git a/chimere/admin.py b/chimere/admin.py
index c36fd26..d492a4c 100644
--- a/chimere/admin.py
+++ b/chimere/admin.py
@@ -84,14 +84,18 @@ def export_to_shapefile(modeladmin, request, queryset):
return response
export_to_shapefile.short_description = _(u"Export to Shapefile")
-def export_to_csv(modeladmin, request, queryset):
- u"""
- Export data to CSV
- """
- filename, result = CSVManager.export(queryset)
- response = HttpResponse(result, mimetype='text/csv')
- response['Content-Disposition'] = 'attachment; filename=%s' % filename
- return response
+def _export_to_csv(cols=[]):
+ def func(modeladmin, request, queryset):
+ u"""
+ Export data to CSV
+ """
+ filename, result = CSVManager.export(queryset, cols=cols)
+ response = HttpResponse(result, mimetype='text/csv')
+ response['Content-Disposition'] = 'attachment; filename=%s' % filename
+ return response
+ return func
+
+export_to_csv = _export_to_csv()
export_to_csv.short_description = _(u"Export to CSV")
def managed_modified(modeladmin, request, queryset):
diff --git a/chimere/utils.py b/chimere/utils.py
index f5bc44f..028da06 100644
--- a/chimere/utils.py
+++ b/chimere/utils.py
@@ -554,10 +554,11 @@ class CSVManager(ImportManager):
return (new_item, updated_item, msg)
@classmethod
- def export(cls, queryset):
+ def export(cls, queryset, cols=[]):
dct = {'description':unicode(datetime.date.today()), 'data':[]}
cls_name = queryset.model.__name__.lower()
- cols = list(cls.COLS)
+ if not cols:
+ cols = list(cls.COLS)
for pm in queryset.model.all_properties():
cols.append((pm.name, pm.getAttrName(), pm.getAttrName()+'_set'))
header = [col[0] for col in cols]
@@ -568,7 +569,10 @@ class CSVManager(ImportManager):
if callable(attr):
data.append(attr(item))
else:
- data.append(getattr(item, attr))
+ v = getattr(item, attr)
+ if v == None:
+ v = ''
+ data.append(v)
dct['data'].append(data)
filename = unicode_normalize(settings.PROJECT_NAME + dct['description']\
+ '.csv')
--
cgit v1.2.3
From 2bfbae53d5f59cb38388070cc31ca12dbfe40487 Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Tue, 23 Apr 2013 00:30:32 +0200
Subject: Minor fix on CSV export
---
chimere/utils.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/chimere/utils.py b/chimere/utils.py
index 028da06..f86e5db 100644
--- a/chimere/utils.py
+++ b/chimere/utils.py
@@ -559,8 +559,9 @@ class CSVManager(ImportManager):
cls_name = queryset.model.__name__.lower()
if not cols:
cols = list(cls.COLS)
- for pm in queryset.model.all_properties():
- cols.append((pm.name, pm.getAttrName(), pm.getAttrName()+'_set'))
+ if hasattr(queryset.model, 'all_properties'):
+ for pm in queryset.model.all_properties():
+ cols.append((pm.name, pm.getAttrName(), pm.getAttrName()+'_set'))
header = [col[0] for col in cols]
dct['data'].append(header)
for item in queryset.all():
--
cgit v1.2.3
From 1ffe63b5ab64a51dd7f3c3ddcb98e770b32b74d3 Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Tue, 23 Apr 2013 17:47:23 +0200
Subject: Allow to load static JSON overlay layers
---
chimere/static/chimere/js/jquery.chimere-leaflet.js | 18 ++++++++++++++++--
chimere/urls.py | 4 +++-
chimere/views.py | 4 ++++
3 files changed, 23 insertions(+), 3 deletions(-)
diff --git a/chimere/static/chimere/js/jquery.chimere-leaflet.js b/chimere/static/chimere/js/jquery.chimere-leaflet.js
index b48a54e..f0ae75f 100644
--- a/chimere/static/chimere/js/jquery.chimere-leaflet.js
+++ b/chimere/static/chimere/js/jquery.chimere-leaflet.js
@@ -105,7 +105,8 @@ See the file COPYING for details.
'marker-cluster-large',
'marker-cluster-xlarge',
'marker-cluster-xxlarge'
- )
+ ),
+ extra_json_data: null
};
var settings = {};
/*
@@ -126,12 +127,13 @@ See the file COPYING for details.
for (idx=0 ; idx < settings.map_layers.length ; idx++){
map_layers[settings.map_layer_names[idx]] = settings.map_layers[idx];
}
- L.control.layers(map_layers).addTo(map);
+ settings.layer_control = L.control.layers(map_layers).addTo(map);
if(settings.zoom && settings.lat && settings.lon){
map.setView([settings.lat, settings.lon], settings.zoom);
} else {
map.fitWorld();
}
+
settings.icons = new Object();
if (settings.enable_clustering){
settings.NumberedDivIcon = L.Icon.extend({
@@ -246,6 +248,18 @@ See the file COPYING for details.
methods.loadCategories();
methods.loadGeoObjects();
+ if (settings.extra_json_data){
+ for (idx=0;idx
+# Copyright (C) 2008-2013 Étienne Loks
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as
@@ -103,6 +103,8 @@ urlpatterns += patterns('chimere.views',
'processRouteFile', name='process_route_file'),
url(r'^(?P[a-zA-Z0-9_-]+/)?dyn/(?P\w+)/$',
'extraPage', name='extra_page'),
+ url(r'^(?P[a-zA-Z0-9_-]+/)?json/(?P[a-zA-Z0-9_-]+)/(?P[a-zA-Z0-9_-]+).json$', 'get_json',
+ name='get-json'),
# At the end, because it catches large
url(r'^(?P[a-zA-Z0-9_-]+)?', 'index', name="index"),
)
diff --git a/chimere/views.py b/chimere/views.py
index 136f34a..f58b7ec 100644
--- a/chimere/views.py
+++ b/chimere/views.py
@@ -828,6 +828,10 @@ def route(request, area_name, lon1, lat1, lonlat_steps, lon2, lat2,
message)
return HttpResponse(data)
+def get_json(request, area_name='', app_name='', filename=''):
+ return HttpResponse(open(settings.STATIC_ROOT+app_name+'/json/'+filename+'.json'),
+ 'application/javascript', status=200)
+
def rss(request, area_name=''):
'''
Redirect to RSS subscription page
--
cgit v1.2.3
From fc1182be7f97361f486c82ac61ff38a06371c53d Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Wed, 24 Apr 2013 01:52:42 +0200
Subject: Fix overlay layer loading when many are available
---
chimere/static/chimere/js/jquery.chimere-leaflet.js | 18 +++++++++++-------
1 file changed, 11 insertions(+), 7 deletions(-)
diff --git a/chimere/static/chimere/js/jquery.chimere-leaflet.js b/chimere/static/chimere/js/jquery.chimere-leaflet.js
index f0ae75f..c67a7a1 100644
--- a/chimere/static/chimere/js/jquery.chimere-leaflet.js
+++ b/chimere/static/chimere/js/jquery.chimere-leaflet.js
@@ -250,13 +250,17 @@ See the file COPYING for details.
methods.loadGeoObjects();
if (settings.extra_json_data){
for (idx=0;idx
Date: Sat, 15 Jun 2013 22:03:05 +0200
Subject: Refactoring: renaming Area to Map
---
chimere/actions.py | 12 +-
chimere/admin.py | 30 +-
chimere/feeds.py | 14 +-
chimere/forms.py | 20 +-
chimere/migrations/0005_rename_area_to_map.py | 295 +
chimere/models.py | 136 +-
chimere/scripts/upgrade.py | 315 -
chimere/static/chimere/css/styles.css | 20 +-
chimere/static/chimere/js/base.js | 14 +-
.../static/chimere/js/jquery.chimere-leaflet.js | 4 +-
chimere/static/chimere/js/jquery.chimere-ol.js | 6 +-
chimere/static/leaflet/leaflet-src.js | 8338 ++++++++++++++++++++
chimere/templates/chimere/base.html | 4 +-
chimere/templates/chimere/blocks/areas.html | 26 +-
.../chimere/blocks/areas_alternative.html | 11 -
chimere/templates/chimere/blocks/head_chimere.html | 4 +-
.../templates/chimere/blocks/live_coordinates.html | 2 +-
chimere/templates/chimere/blocks/map.html | 10 +-
chimere/templates/chimere/blocks/maps.html | 25 +
.../templates/chimere/blocks/maps_alternative.html | 11 +
chimere/templates/chimere/blocks/news.html | 2 +-
chimere/templates/chimere/blocks/welcome.html | 2 +-
chimere/templates/chimere/detail.html | 2 +-
chimere/templates/chimere/feeds/rss.html | 10 +-
chimere/templates/chimere/main_map.html | 6 +-
chimere/templatetags/chimere_tags.py | 112 +-
chimere/urls.py | 56 +-
chimere/views.py | 202 +-
chimere/widgets.py | 48 +-
29 files changed, 9033 insertions(+), 704 deletions(-)
create mode 100644 chimere/migrations/0005_rename_area_to_map.py
delete mode 100755 chimere/scripts/upgrade.py
create mode 100644 chimere/static/leaflet/leaflet-src.js
mode change 100644 => 120000 chimere/templates/chimere/blocks/areas.html
delete mode 100644 chimere/templates/chimere/blocks/areas_alternative.html
create mode 100644 chimere/templates/chimere/blocks/maps.html
create mode 100644 chimere/templates/chimere/blocks/maps_alternative.html
diff --git a/chimere/actions.py b/chimere/actions.py
index 8ef5338..7a4b263 100644
--- a/chimere/actions.py
+++ b/chimere/actions.py
@@ -32,9 +32,9 @@ class Action:
self.id, self.path, self.label = id, path, label
self.extra_url_args, self.url = extra_url_args, None
- def update_url(self, area_name):
+ def update_url(self, map_name):
self.url = reverse(self.path,
- args=[area_name + '/' if area_name else ''] + self.extra_url_args)
+ args=[map_name + '/' if map_name else ''] + self.extra_url_args)
default_actions = [(Action('view', 'chimere:index', _('View')), []),
(Action('contribute', 'chimere:edit', _('Contribute')),
@@ -51,15 +51,15 @@ if settings.EMAIL_HOST:
_('Contact us')), []),)
-def actions(area_name=''):
+def actions(map_name=''):
acts = default_actions[:]
for act, childs in default_actions:
- act.update_url(area_name)
+ act.update_url(map_name)
for child_act in childs:
- child_act.update_url(area_name)
+ child_act.update_url(map_name)
for page in Page.objects.filter(available=True).order_by('order'):
act = Action(page.mnemonic, 'chimere:extra_page', page.title,
[page.mnemonic])
- act.update_url(area_name)
+ act.update_url(map_name)
acts.append((act, []))
return acts
diff --git a/chimere/admin.py b/chimere/admin.py
index d492a4c..f30c00b 100644
--- a/chimere/admin.py
+++ b/chimere/admin.py
@@ -36,14 +36,14 @@ try:
except ImportError:
pass
-from chimere.forms import MarkerAdminForm, RouteAdminForm, AreaAdminForm,\
+from chimere.forms import MarkerAdminForm, RouteAdminForm, MapAdminForm,\
NewsAdminForm, CategoryAdminForm, ImporterAdminForm, OSMForm, \
PageAdminForm, PictureFileAdminForm, MultimediaFileAdminForm
from chimere.models import Category, Icon, SubCategory, Marker, \
- PropertyModel, News, Route, Area, ColorTheme, Color, \
- MultimediaFile, PictureFile, Importer, Layer, AreaLayers,\
+ PropertyModel, News, Route, Map, ColorTheme, Color, \
+ MultimediaFile, PictureFile, Importer, Layer, MapLayers,\
PropertyModelChoice, MultimediaExtension, Page,\
- get_areas_for_user, get_users_by_area
+ get_maps_for_user, get_users_by_map
from chimere.utils import unicode_normalize, ShapefileManager, KMLManager,\
CSVManager
@@ -232,10 +232,10 @@ class MarkerAdmin(admin.ModelAdmin):
def queryset(self, request):
qs = self.model._default_manager.get_query_set()
if not request.user.is_superuser:
- areas = get_areas_for_user(request.user)
+ maps = get_maps_for_user(request.user)
contained = Q()
- for area in areas:
- contained = contained | area.getIncludeMarker()
+ for map in maps:
+ contained = contained | map.getIncludeMarker()
qs = qs.filter(contained)
ordering = self.ordering or ()
if ordering:
@@ -296,10 +296,10 @@ class RouteAdmin(MarkerAdmin):
def queryset(self, request):
qs = self.model._default_manager.get_query_set()
if not request.user.is_superuser:
- areas = get_areas_for_user(request.user)
+ maps = get_maps_for_user(request.user)
contained = Q()
- for area in areas:
- contained = contained | area.getIncludeRoute()
+ for map in maps:
+ contained = contained | map.getIncludeRoute()
qs = qs.filter(contained)
ordering = self.ordering or ()
if ordering:
@@ -314,14 +314,14 @@ class RouteAdmin(MarkerAdmin):
Route.objects.filter(pk=item_id))
class LayerInline(admin.TabularInline):
- model = AreaLayers
+ model = MapLayers
extra = 1
-class AreaAdmin(admin.ModelAdmin):
+class MapAdmin(admin.ModelAdmin):
"""
- Specialized the area field.
+ Specialized the map field.
"""
- form = AreaAdminForm
+ form = MapAdminForm
exclude = ['upper_left_corner', 'lower_right_corner']
inlines = [LayerInline]
list_display = ['name', 'order', 'available', 'default']
@@ -447,6 +447,6 @@ admin.site.register(Marker, MarkerAdmin)
admin.site.register(Route, RouteAdmin)
if not settings.CHIMERE_HIDE_PROPERTYMODEL:
admin.site.register(PropertyModel, PropertyModelAdmin)
-admin.site.register(Area, AreaAdmin)
+admin.site.register(Map, MapAdmin)
admin.site.register(ColorTheme, ColorThemeAdmin)
admin.site.register(Layer)
diff --git a/chimere/feeds.py b/chimere/feeds.py
index 53444ac..2a62da3 100644
--- a/chimere/feeds.py
+++ b/chimere/feeds.py
@@ -29,7 +29,7 @@ from django.utils.translation import ugettext as _
from django.shortcuts import get_object_or_404
-from chimere.models import Category, SubCategory, Marker, Area
+from chimere.models import Category, SubCategory, Marker, Map
class BaseFeed(Feed):
"""
@@ -64,7 +64,7 @@ class LatestPOIsByCategory(BaseFeed):
title_template = "chimere/feeds/rss_title.html"
description_template = "chimere/feeds/rss_descr.html"
- def get_object(self, request, category_id, area_name=''):
+ def get_object(self, request, category_id, map_name=''):
return get_object_or_404(Category, id=category_id)
def title(self, obj):
@@ -99,7 +99,7 @@ class LatestPOIsBySubCategory(BaseFeed):
title_template = "chimere/feeds/rss_title.html"
description_template = "chimere/feeds/rss_descr.html"
- def get_object(self, request, category_id, area_name=''):
+ def get_object(self, request, category_id, map_name=''):
return get_object_or_404(SubCategory, id=category_id)
def title(self, obj):
@@ -149,7 +149,7 @@ class LatestPOIsByZone(BaseFeed):
lower_right_lat = 0
lower_right_lon = 0
- def get_object(self, request, area, area_name=''):
+ def get_object(self, request, area, map_name=''):
"""
Get the extra url. Parameters are the coordinates of the zone (the
upper left and lower right points)
@@ -207,8 +207,8 @@ class LatestPOIsByZoneID(BaseFeed):
title_template = "chimere/feeds/rss_title.html"
description_template = "chimere/feeds/rss_descr.html"
- def get_object(self, request, area_id, area_name=''):
- return get_object_or_404(Area, id=area_id)
+ def get_object(self, request, map_id, map_name=''):
+ return get_object_or_404(Map, id=map_id)
def title(self, obj):
return settings.PROJECT_NAME + u" - " + \
@@ -217,7 +217,7 @@ class LatestPOIsByZoneID(BaseFeed):
def link(self, obj):
if not obj:
raise FeedDoesNotExist
- return reverse('chimere:feeds-areaid', args=['', obj.id])
+ return reverse('chimere:feeds-mapid', args=['', obj.id])
def items(self, obj):
q = Marker.objects.filter(available_date__isnull=False, status='A')
diff --git a/chimere/forms.py b/chimere/forms.py
index ea23f94..b6d5feb 100644
--- a/chimere/forms.py
+++ b/chimere/forms.py
@@ -31,7 +31,7 @@ from django.contrib.auth.models import User, Permission, ContentType
from django.contrib.admin.widgets import AdminDateWidget
from django.core.mail import EmailMessage, BadHeaderError
-from chimere.models import Marker, Route, PropertyModel, Property, Area,\
+from chimere.models import Marker, Route, PropertyModel, Property, Map,\
News, Category, SubCategory, RouteFile, MultimediaFile, MultimediaType, \
PictureFile, Importer, PropertyModelChoice, IFRAME_LINKS, \
MultimediaExtension, Page, IMPORTER_CHOICES
@@ -514,7 +514,7 @@ class FullFileForm(FileForm):
super(FullFileForm, self).__init__(*args, **kwargs)
self.fields.keyOrder = ['name', 'raw_file']
-class AreaAdminForm(forms.ModelForm):
+class MapAdminForm(forms.ModelForm):
"""
Admin page to create an area
"""
@@ -522,7 +522,7 @@ class AreaAdminForm(forms.ModelForm):
welcome_message = forms.CharField(widget=TextareaAdminWidget,
required=False)
class Meta:
- model = Area
+ model = Map
def __init__(self, *args, **keys):
"""
@@ -547,7 +547,7 @@ class AreaAdminForm(forms.ModelForm):
keys['initial'].update(dct)
else:
keys['initial'] = dct
- super(AreaAdminForm, self).__init__(*args, **keys)
+ super(MapAdminForm, self).__init__(*args, **keys)
def clean(self):
'''
@@ -561,7 +561,7 @@ class AreaAdminForm(forms.ModelForm):
msg = _(u"No area selected.")
raise forms.ValidationError(msg)
if self.cleaned_data.get('order'):
- q = Area.objects.filter(order=self.cleaned_data.get('order'))
+ q = Map.objects.filter(order=self.cleaned_data.get('order'))
if self.instance:
q = q.exclude(pk=self.instance.pk)
if q.count():
@@ -574,7 +574,7 @@ class AreaAdminForm(forms.ModelForm):
"""
Custom save method in order to manage area
"""
- new_area = super(AreaAdminForm, self).save(*args, **keys)
+ new_area = super(MapAdminForm, self).save(*args, **keys)
area = self.cleaned_data['area']
new_area.upper_left_corner = 'POINT(%s %s)' % (area[0][0], area[0][1])
new_area.lower_right_corner = 'POINT(%s %s)' % (area[1][0],
@@ -582,7 +582,7 @@ class AreaAdminForm(forms.ModelForm):
content_type = ContentType.objects.get(app_label="chimere",
model="area")
if new_area.urn:
- mnemo = 'change_area_' + new_area.urn
+ mnemo = 'change_map_' + new_area.urn
perm = Permission.objects.filter(codename=mnemo)
if not perm:
perm = Permission(name='Can change ' + new_area.name,
@@ -590,18 +590,18 @@ class AreaAdminForm(forms.ModelForm):
perm.save()
else:
if 'urn' in self.initial:
- mnemo = 'change_area_' + self.initial['urn']
+ mnemo = 'change_map_' + self.initial['urn']
perm = Permission.objects.filter(codename=mnemo)
if perm:
perm[0].delete()
return new_area
-class AreaForm(AreaAdminForm):
+class MapForm(MapAdminForm):
"""
Form for the edit page
"""
class Meta:
- model = Area
+ model = Map
CHIMERE_ROUTING_TRANSPORT = []
ROUTING_INIT = None
diff --git a/chimere/migrations/0005_rename_area_to_map.py b/chimere/migrations/0005_rename_area_to_map.py
new file mode 100644
index 0000000..0a3af14
--- /dev/null
+++ b/chimere/migrations/0005_rename_area_to_map.py
@@ -0,0 +1,295 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ db.rename_table('chimere_area', 'chimere_map')
+ db.rename_table('chimere_arealayers', 'chimere_maplayers')
+ db.rename_column('chimere_maplayers', 'area_id', 'map_id')
+ db.rename_table('chimere_subcategory_areas', 'chimere_subcategory_maps')
+ db.rename_column('chimere_subcategory_maps', 'area_id', 'map_id')
+ db.rename_table('chimere_area_default_subcategories',
+ 'chimere_map_default_subcategories')
+ db.rename_column('chimere_map_default_subcategories', 'area_id',
+ 'map_id')
+ db.rename_table('chimere_news_areas', 'chimere_news_maps')
+ db.rename_column('chimere_news_maps', 'area_id', 'map_id')
+
+ def backwards(self, orm):
+ db.rename_table('chimere_map', 'chimere_area')
+ db.rename_table('chimere_maplayers', 'chimere_arealayers')
+ db.rename_column('chimere_arealayers', 'map_id', 'area_id')
+ db.rename_table('chimere_subcategory_maps', 'chimere_subcategory_areas')
+ db.rename_column('chimere_subcategory_areas', 'map_id', 'area_id')
+ db.rename_table('chimere_map_default_subcategories',
+ 'chimere_area_default_subcategories')
+ db.rename_column('chimere_area_default_subcategories', 'map_id',
+ 'area_id')
+ db.rename_table('chimere_news_maps',
+ 'chimere_news_areas ')
+ db.rename_column('chimere_news_maps', 'map_id', 'area_id')
+
+ models = {
+ 'chimere.aggregatedroute': {
+ 'Meta': {'object_name': 'AggregatedRoute', 'db_table': "'chimere_aggregated_routes'", 'managed': 'False'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'route': ('django.contrib.gis.db.models.fields.MultiLineStringField', [], {}),
+ 'status': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'subcategory': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.SubCategory']"})
+ },
+ 'chimere.category': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'Category'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.color': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'Color'},
+ 'code': ('django.db.models.fields.CharField', [], {'max_length': '6'}),
+ 'color_theme': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'colors'", 'to': "orm['chimere.ColorTheme']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.colortheme': {
+ 'Meta': {'object_name': 'ColorTheme'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.icon': {
+ 'Meta': {'object_name': 'Icon'},
+ 'height': ('django.db.models.fields.IntegerField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'width': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.importer': {
+ 'Meta': {'object_name': 'Importer'},
+ 'associate_marker_to_way': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'categories': ('chimere.widgets.SelectMultipleField', [], {'to': "orm['chimere.SubCategory']", 'symmetrical': 'False'}),
+ 'default_description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'default_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'filtr': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'get_description': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'importer_type': ('django.db.models.fields.CharField', [], {'max_length': '4'}),
+ 'license': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'origin': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'overwrite': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'source': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'source_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'srid': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'state': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'zipped': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'chimere.layer': {
+ 'Meta': {'object_name': 'Layer'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layer_code': ('django.db.models.fields.TextField', [], {'max_length': '300'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.map': {
+ 'Meta': {'ordering': "('order', 'name')", 'object_name': 'Map'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'default': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'default_subcategories': ('chimere.widgets.SelectMultipleField', [], {'to': "orm['chimere.SubCategory']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'dynamic_categories': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'external_css': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layers': ('chimere.widgets.SelectMultipleField', [], {'symmetrical': 'False', 'related_name': "'maps'", 'blank': 'True', 'through': "orm['chimere.MapLayers']", 'to': "orm['chimere.Layer']"}),
+ 'lower_right_corner': ('django.contrib.gis.db.models.fields.PointField', [], {'default': "'POINT(0 0)'"}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'unique': 'True'}),
+ 'restrict_to_extent': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'subcategories': ('chimere.widgets.SelectMultipleField', [], {'symmetrical': 'False', 'related_name': "'maps'", 'blank': 'True', 'db_table': "'chimere_subcategory_maps'", 'to': "orm['chimere.SubCategory']"}),
+ 'upper_left_corner': ('django.contrib.gis.db.models.fields.PointField', [], {'default': "'POINT(0 0)'"}),
+ 'urn': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'blank': 'True'}),
+ 'welcome_message': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.maplayers': {
+ 'Meta': {'ordering': "('order',)", 'object_name': 'MapLayers'},
+ 'default': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.Layer']"}),
+ 'map': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.Map']"}),
+ 'order': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.marker': {
+ 'Meta': {'ordering': "('status', 'name')", 'object_name': 'Marker'},
+ 'available_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'categories': ('chimere.widgets.SelectMultipleField', [], {'to': "orm['chimere.SubCategory']", 'symmetrical': 'False'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'end_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'import_key': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'import_source': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'import_version': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'is_front_page': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'license': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'modified_since_import': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'not_for_osm': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'origin': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'point': ('chimere.widgets.PointField', [], {}),
+ 'ref_item': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'submited_marker'", 'null': 'True', 'to': "orm['chimere.Marker']"}),
+ 'route': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'associated_marker'", 'null': 'True', 'to': "orm['chimere.Route']"}),
+ 'start_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'status': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'submiter_comment': ('django.db.models.fields.TextField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'submiter_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
+ 'submiter_name': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
+ 'submiter_session_key': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
+ 'weight': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.multimediaextension': {
+ 'Meta': {'object_name': 'MultimediaExtension'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'multimedia_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'extensions'", 'to': "orm['chimere.MultimediaType']"}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '6'})
+ },
+ 'chimere.multimediafile': {
+ 'Meta': {'object_name': 'MultimediaFile'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'marker': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'multimedia_files'", 'to': "orm['chimere.Marker']"}),
+ 'miniature': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'multimedia_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.MultimediaType']", 'null': 'True', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'url': ('django.db.models.fields.URLField', [], {'max_length': '200'})
+ },
+ 'chimere.multimediatype': {
+ 'Meta': {'object_name': 'MultimediaType'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'iframe': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'media_type': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'mime_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.news': {
+ 'Meta': {'object_name': 'News'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'content': ('django.db.models.fields.TextField', [], {}),
+ 'date': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_front_page': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'maps': ('chimere.widgets.SelectMultipleField', [], {'symmetrical': 'False', 'to': "orm['chimere.Map']", 'null': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.page': {
+ 'Meta': {'object_name': 'Page'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'content': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mnemonic': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '10', 'null': 'True', 'blank': 'True'}),
+ 'template_path': ('django.db.models.fields.CharField', [], {'max_length': '150', 'null': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.picturefile': {
+ 'Meta': {'object_name': 'PictureFile'},
+ 'height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'marker': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'pictures'", 'to': "orm['chimere.Marker']"}),
+ 'miniature': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'picture': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
+ 'thumbnailfile': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'thumbnailfile_height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'thumbnailfile_width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.property': {
+ 'Meta': {'object_name': 'Property'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'marker': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.Marker']"}),
+ 'propertymodel': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.PropertyModel']"}),
+ 'value': ('django.db.models.fields.TextField', [], {})
+ },
+ 'chimere.propertymodel': {
+ 'Meta': {'ordering': "('order',)", 'object_name': 'PropertyModel'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mandatory': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {}),
+ 'subcategories': ('chimere.widgets.SelectMultipleField', [], {'symmetrical': 'False', 'related_name': "'properties'", 'blank': 'True', 'to': "orm['chimere.SubCategory']"}),
+ 'type': ('django.db.models.fields.CharField', [], {'max_length': '1'})
+ },
+ 'chimere.propertymodelchoice': {
+ 'Meta': {'object_name': 'PropertyModelChoice'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'propertymodel': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'choices'", 'to': "orm['chimere.PropertyModel']"}),
+ 'value': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.route': {
+ 'Meta': {'ordering': "('status', 'name')", 'object_name': 'Route'},
+ 'associated_file': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.RouteFile']", 'null': 'True', 'blank': 'True'}),
+ 'categories': ('chimere.widgets.SelectMultipleField', [], {'to': "orm['chimere.SubCategory']", 'symmetrical': 'False'}),
+ 'end_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'has_associated_marker': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'import_key': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'import_source': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'import_version': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'license': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'modified_since_import': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'not_for_osm': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'origin': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'picture': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'ref_item': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'submited_route'", 'null': 'True', 'to': "orm['chimere.Route']"}),
+ 'route': ('chimere.widgets.RouteField', [], {}),
+ 'start_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'status': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'submiter_comment': ('django.db.models.fields.TextField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'submiter_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
+ 'submiter_name': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
+ 'submiter_session_key': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
+ 'width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.routefile': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'RouteFile'},
+ 'file_type': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'raw_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+ 'simplified_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.subcategory': {
+ 'Meta': {'ordering': "['category', 'order']", 'object_name': 'SubCategory'},
+ 'as_layer': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'category': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'subcategories'", 'to': "orm['chimere.Category']"}),
+ 'color_theme': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.ColorTheme']", 'null': 'True', 'blank': 'True'}),
+ 'dated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'hover_icon': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'subcat_hovered'", 'null': 'True', 'to': "orm['chimere.Icon']"}),
+ 'icon': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.Icon']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'item_type': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '1000'}),
+ 'routing_warn': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'submission': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'weighted': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'chimere.tinyurl': {
+ 'Meta': {'object_name': 'TinyUrl'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'parameters': ('django.db.models.fields.CharField', [], {'max_length': '500'})
+ }
+ }
+
+ complete_apps = ['chimere']
diff --git a/chimere/models.py b/chimere/models.py
index 7db530a..60e4cd1 100644
--- a/chimere/models.py
+++ b/chimere/models.py
@@ -105,7 +105,7 @@ class News(models.Model):
date = models.DateField(_(u"Date"), auto_now_add=True)
content = models.TextField()
url = models.URLField(_(u"Url"), max_length=200, blank=True, null=True)
- areas = SelectMultipleField('Area', verbose_name=_(u"Associated areas"),
+ maps = SelectMultipleField('Map', verbose_name=_(u"Associated maps"),
blank=True, null=True)
def __unicode__(self):
ordering = ["-date"]
@@ -239,7 +239,7 @@ class SubCategory(models.Model):
verbose_name_plural = _(u"Sub-categories")
@classmethod
- def getAvailable(cls, item_types=None, area_name=None, public=False):
+ def getAvailable(cls, item_types=None, map_name=None, public=False):
'''Get list of tuples with first the category and second the associated
subcategories
'''
@@ -252,14 +252,14 @@ class SubCategory(models.Model):
if public:
subcategories = subcategories.filter(submission=True)
selected_cats = []
- if area_name:
- area = Area.objects.get(urn=area_name)
+ if map_name:
+ map = Map.objects.get(urn=map_name)
# if there some restrictions with categories limit them
- if area.subcategories.count():
- sub_ids = [sub.id for sub in area.subcategories.all()]
+ if map.subcategories.count():
+ sub_ids = [sub.id for sub in map.subcategories.all()]
subcategories = subcategories.filter(id__in=sub_ids)
selected_cats = [subcat.pk
- for subcat in area.default_subcategories.all()]
+ for subcat in map.default_subcategories.all()]
for sub_category in subcategories.order_by('order'):
if sub_category.category not in sub_categories:
sub_categories[sub_category.category] = []
@@ -275,9 +275,9 @@ class SubCategory(models.Model):
return subcategories
@classmethod
- def getAvailableTuples(cls, area_name=None):
+ def getAvailableTuples(cls, map_name=None):
cats = []
- for cat, subcats in cls.getAvailable(area_name=area_name):
+ for cat, subcats in cls.getAvailable(map_name=map_name):
cats.append((unicode(cat),
[(subcat.pk, subcat.name) for subcat in subcats]))
return cats
@@ -679,20 +679,20 @@ class Marker(GeographicItem):
if cats.count():
return cats.all()[0]
- def get_absolute_url(self, area_name=''):
+ def get_absolute_url(self, map_name=''):
parameters = 'current_feature=%d' % self.id
if self.default_category:
parameters += '&checked_categories=%s' % self.default_category.pk
urn = TinyUrl.getUrnByParameters(parameters)
- area_name = area_name + '/' if area_name else ''
- url = reverse('chimere:tiny', args=[area_name, urn])
+ map_name = map_name + '/' if map_name else ''
+ url = reverse('chimere:tiny', args=[map_name, urn])
return url
PRE_ATTRS = {
'Marker':('name', 'geometry', 'import_version', 'modified_since_import'),
'Route':('name', 'geometry', 'import_version', 'modified_since_import'),
- 'Area':('urn', 'name'),
+ 'Map':('urn', 'name'),
}
def geometry_pre_save(cls, pre_save_geom_values):
def geom_pre_save(sender, **kwargs):
@@ -1278,14 +1278,14 @@ class SimpleArea:
return True
return False
- def getCategories(self, status='A', filter_available=True, area_name=None):
+ def getCategories(self, status='A', filter_available=True, map_name=None):
"""
- Get categories for this area
+ Get categories for this map
"""
wheres = []
- if area_name:
+ if map_name:
subcategory_pks = []
- for cat, subcats in SubCategory.getAvailable(area_name=area_name):
+ for cat, subcats in SubCategory.getAvailable(map_name=map_name):
for subcat in subcats:
subcategory_pks.append(unicode(subcat.pk))
if filter_available:
@@ -1349,11 +1349,11 @@ class Layer(models.Model):
class Meta:
verbose_name = _("Layer")
-class Area(models.Model, SimpleArea):
- """Rectangular area of the map
+class Map(models.Model, SimpleArea):
+ """A map
"""
name = models.CharField(_(u"Name"), max_length=150)
- urn = models.SlugField(_(u"Area urn"), max_length=50, blank=True,
+ urn = models.SlugField(_(u"Map urn"), max_length=50, blank=True,
unique=True)
welcome_message = models.TextField(_(u"Welcome message"), blank=True,
null=True)
@@ -1363,18 +1363,18 @@ class Area(models.Model, SimpleArea):
default='POINT(0 0)', srid=settings.CHIMERE_EPSG_DISPLAY_PROJECTION)
lower_right_corner = models.PointField(_(u"Lower right corner"),
default='POINT(0 0)', srid=settings.CHIMERE_EPSG_DISPLAY_PROJECTION)
- default = models.NullBooleanField(_(u"Default area"),
- help_text=_(u"Only one area is set by default"))
- layers = SelectMultipleField(Layer, related_name='areas',
- through='AreaLayers', blank=True)
+ default = models.NullBooleanField(_(u"Default map"),
+ help_text=_(u"Only one map is set by default"))
+ layers = SelectMultipleField(Layer, related_name='maps',
+ through='MapLayers', blank=True)
default_subcategories = SelectMultipleField(SubCategory, blank=True,
verbose_name=_(u"Sub-categories checked by default"))
dynamic_categories = models.NullBooleanField(
_(u"Sub-categories dynamicaly displayed"),
help_text=_(u"If checked, categories are only displayed in the menu if "
u"they are available on the current extent."))
- subcategories = SelectMultipleField(SubCategory, related_name='areas',
- blank=True, db_table='chimere_subcategory_areas',
+ subcategories = SelectMultipleField(SubCategory, related_name='maps',
+ blank=True, db_table='chimere_subcategory_maps',
verbose_name=_(u"Restricted to theses sub-categories"),
help_text=_(u"If no sub-category is set all sub-categories are "
u"available"))
@@ -1389,11 +1389,11 @@ class Area(models.Model, SimpleArea):
class Meta:
ordering = ('order', 'name')
- verbose_name = _("Area")
+ verbose_name = _("Map")
@classmethod
def getAvailable(cls):
- '''Get available areas
+ '''Get available maps
'''
return cls.objects.filter(available=True)
@@ -1419,54 +1419,64 @@ class Area(models.Model, SimpleArea):
"""
return Q(route__contained=self.getWkt())
-pre_save_area_values = {}
-def area_pre_save(sender, **kwargs):
+pre_save_map_values = {}
+def map_pre_save(sender, **kwargs):
if not kwargs['instance']:
return
- geometry_pre_save(Area, pre_save_area_values)(sender, **kwargs)
-pre_save.connect(area_pre_save, sender=Area)
+ geometry_pre_save(Map, pre_save_map_values)(sender, **kwargs)
+pre_save.connect(map_pre_save, sender=Map)
-def area_post_save(sender, **kwargs):
+def map_post_save(sender, **kwargs):
if not kwargs['instance']:
return
- area = kwargs['instance']
- if area.default:
- defaults = Area.objects.filter(default=True).exclude(pk=area.pk)
+ map = kwargs['instance']
+ if map.default:
+ defaults = Map.objects.filter(default=True).exclude(pk=map.pk)
for default in defaults:
default.default = False
default.save()
# manage permissions
- old_urn, old_name = area.urn, area.name
- if area.pk in pre_save_area_values:
- old_urn, old_name = pre_save_area_values[area.pk]
+ old_urn, old_name = map.urn, map.name
+ if map.pk in pre_save_map_values:
+ old_urn, old_name = pre_save_map_values[map.pk]
perm, old_groups, old_users = None, [], []
- if area.urn != old_urn:
- oldmnemo = 'change_area_' + old_urn
+
+ if map.urn != old_urn:
+ oldmnemo = 'change_map_' + old_urn
old_perm = Permission.objects.filter(codename=oldmnemo)
if old_perm.count():
perm = old_perm.all()[0]
- perm.codename = 'change_area_' + area.urn
+ perm.codename = 'change_map_' + map.urn
perm.save()
- if not area.urn:
- area.urn = defaultfilters.slugify(area.name)
- area.save()
- mnemo = 'change_area_' + area.urn
+ if not map.urn:
+ map.urn = defaultfilters.slugify(map.name)
+ map.save()
+
+ # fix old mnemo
+ oldmnemo = 'change_area_' + old_urn
+ old_perm = Permission.objects.filter(codename=oldmnemo)
+ if old_perm.count():
+ perm = old_perm.all()[0]
+ perm.codename = 'change_map_' + map.urn
+ perm.save()
+
+ mnemo = 'change_map_' + map.urn
perm = Permission.objects.filter(codename=mnemo)
- lbl = "Can change " + area.name
+ lbl = "Can change " + map.name
if not perm.count():
content_type, created = ContentType.objects.get_or_create(
- app_label="chimere", model="area")
+ app_label="chimere", model="map")
perm = Permission(name=lbl, content_type_id=content_type.id,
codename=mnemo)
perm.save()
else:
perm = perm.all()[0]
- if old_name != area.name:
+ if old_name != map.name:
perm.name = lbl
perm.save()
# manage moderation group
- groupname = area.name + " moderation"
- if old_name != area.name:
+ groupname = map.name + " moderation"
+ if old_name != map.name:
old_groupname = old_name + " moderation"
old_gp = Group.objects.filter(name=old_groupname)
if old_gp.count():
@@ -1487,33 +1497,33 @@ def area_post_save(sender, **kwargs):
for p in Permission.objects.filter(content_type=ct).all():
group.permissions.add(p)
-post_save.connect(area_post_save, sender=Area)
+post_save.connect(map_post_save, sender=Map)
-def get_areas_for_user(user):
+def get_maps_for_user(user):
"""
Getting subcats for a specific user
"""
perms = user.get_all_permissions()
- areas = set()
- prefix = 'chimere.change_area_'
+ maps = set()
+ prefix = 'chimere.change_map_'
for perm in perms:
if perm.startswith(prefix):
try:
- area = Area.objects.get(urn=perm[len(prefix):])
- areas.add(area)
+ map = Map.objects.get(urn=perm[len(prefix):])
+ maps.add(map)
except ObjectDoesNotExist:
pass
- return areas
+ return maps
-def get_users_by_area(area):
- if not area:
+def get_users_by_map(map):
+ if not map:
return []
- perm = 'change_area_'+area.urn
+ perm = 'change_map_'+map.urn
return User.objects.filter(Q(groups__permissions__codename=perm)|
Q(user_permissions__codename=perm)).all()
-class AreaLayers(models.Model):
- area = models.ForeignKey(Area)
+class MapLayers(models.Model):
+ map = models.ForeignKey(Map)
layer = models.ForeignKey(Layer)
order = models.IntegerField(_(u"Order"))
default = models.NullBooleanField(_(u"Default layer"))
diff --git a/chimere/scripts/upgrade.py b/chimere/scripts/upgrade.py
deleted file mode 100755
index accb751..0000000
--- a/chimere/scripts/upgrade.py
+++ /dev/null
@@ -1,315 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-import sys
-sys.path.append('.')
-
-from django.core.management import setup_environ
-import settings
-
-setup_environ(settings)
-
-from django.db import connection, transaction
-
-cursor = connection.cursor()
-
-from main.models import Area, Marker, Route, Icon, SubCategory
-from django.contrib.gis.geos import LineString
-
-# early versions before 0.1: urn field doesn't exist for area
-
-import htmlentitydefs, re
-
-def slugfy(text, separator):
- ret = u""
- text = text.strip()
- for c in text.lower():
- try:
- ret += htmlentitydefs.codepoint2name[ord(c)][0]
- except:
- ret += c
- ret = re.sub("\W", " ", ret)
- ret = re.sub(" +", separator, ret)
- return ret.strip()
-
-QUERY_CHECK_FIELD = """SELECT a.attname AS field FROM pg_class c, pg_attribute a
- WHERE c.relname = '%s' AND a.attnum > 0 AND a.attrelid = c.oid
- AND a.attname='%s';"""
-QUERY_CHECK_TABLE = """SELECT c.relname FROM pg_class c
-WHERE c.relname = '%s';"""
-
-
-query = QUERY_CHECK_FIELD % ('main_area', 'urn')
-cursor.execute(query)
-transaction.commit_unless_managed()
-
-row = cursor.fetchone()
-if not row:
- query_update = "ALTER TABLE main_area ADD COLUMN urn VARCHAR(50) \
-UNIQUE"
- cursor.execute(query_update)
- transaction.commit_unless_managed()
- areas = Area.objects.all()
- print " * urn field created in table main_area"
- for area in areas:
- urn = slugfy(area.name, "-")
- area.urn = urn
- area.save()
- print " * area %s urn is now: %s" % (area.name, area.urn)
-
- query = "ALTER TABLE main_area ALTER COLUMN urn SET not null;"
- cursor.execute(query)
- transaction.commit_unless_managed()
- print " * urn field has now the constraint NOT NULL"
-
-from django.contrib.auth.models import Permission, ContentType
-
-# check if permission have been correctly set for each areas
-areas = Area.objects.all()
-for area in areas:
- content_type = ContentType.objects.get(app_label="main",
- model="area")
- mnemo = 'change_area_' + area.urn
- perm = Permission.objects.filter(codename=mnemo)
- if not perm:
- lbl = "Can change " + area.name
- perm = Permission(name=lbl, content_type_id=content_type.id,
- codename=mnemo)
- perm.save()
- print ' * permission "' + lbl + '" has been created'
- print " WARNING: don't forget to update administrator's rights with \
-this new permission"
-
-# early versions before 0.1: subcategory_areas table doesn't exist
-# version 1.0 subcategory_areas renamed to main_subcategory_areas
-
-query = QUERY_CHECK_TABLE % 'main_subcategory_areas'
-cursor.execute(query)
-transaction.commit_unless_managed()
-
-row = cursor.fetchone()
-if not row:
- query = QUERY_CHECK_TABLE % 'subcategory_areas'
- cursor.execute(query)
- transaction.commit_unless_managed()
-
- row = cursor.fetchone()
- if row:
- query_rename = "ALTER TABLE subcategory_areas RENAME TO \
-main_subcategory_areas;"
- cursor.execute(query_rename)
- transaction.commit_unless_managed()
- print " * subcategory_areas renamed to main_subcategory_areas"
- else:
- query_create = """
-CREATE TABLE "main_subcategory_areas" (
- "id" serial NOT NULL PRIMARY KEY,
- "subcategory_id" integer NOT NULL REFERENCES "main_subcategory" ("id")
- DEFERRABLE INITIALLY DEFERRED,
- "area_id" integer NOT NULL REFERENCES "main_area" ("id")
- DEFERRABLE INITIALLY DEFERRED,
- UNIQUE ("subcategory_id", "area_id"));
-"""
- cursor.execute(query_create)
- transaction.commit_unless_managed()
- print " * main_subcategory_areas created"
-
-# early versions before 0.1: main_tinyurl table doesn't exist
-
-query = QUERY_CHECK_TABLE % 'main_tinyurl'
-cursor.execute(query)
-transaction.commit_unless_managed()
-
-row = cursor.fetchone()
-if not row:
- query_create = """
-CREATE TABLE "main_tinyurl" (
- "id" serial NOT NULL PRIMARY KEY,
- "parameters" varchar(500) NOT NULL);
-"""
- cursor.execute(query_create)
- transaction.commit_unless_managed()
- print " * main_tinyurl created"
-
-
-# early versions before 0.1: save area with wrong SRID
-# only errors with default SRID is managed adapt the script for your SRID
-
-from osgeo import osr
-
-srs = osr.SpatialReference()
-srs.ImportFromEPSG(4326) # WGS84
-ll = srs.CloneGeogCS()
-srs.ImportFromEPSG(settings.EPSG_PROJECTION)
-proj = osr.CoordinateTransformation(srs, ll)
-
-changed = False
-areas = Area.objects.all()
-for area in areas:
- # only one test: assume each point as been save with the same SRID...
- if area.upper_left_corner.srid == 4326 and area.upper_left_corner.x > 90 \
- or area.upper_left_corner.x < -90:
- changed = True
- pt = proj.TransformPoint(area.upper_left_corner.y,
- area.upper_left_corner.x)
- area.upper_left_corner.x = pt[0]
- area.upper_left_corner.y = pt[1]
- pt = proj.TransformPoint(area.lower_right_corner.y,
- area.lower_right_corner.x)
- area.lower_right_corner.x = pt[0]
- area.lower_right_corner.y = pt[1]
- area.save()
-if changed:
- print " * projections of areas corrected"
-
-# changement from version 1.0 to 1.1: add dated fields to markers and routes
-if settings.DAYS_BEFORE_EVENT:
- for cls in (Marker, Route):
- table = cls._meta.db_table
- query = QUERY_CHECK_FIELD % (table, 'start_date')
- cursor.execute(query)
- transaction.commit_unless_managed()
-
- row = cursor.fetchone()
- if not row:
- query_update = "ALTER TABLE "+table+" ADD COLUMN start_date date"
- cursor.execute(query_update)
- transaction.commit_unless_managed()
- query_update = "ALTER TABLE "+table+" ADD COLUMN end_date date"
- cursor.execute(query_update)
- transaction.commit_unless_managed()
- print " * start_date and end_date added to table " + table + "."
-
-# changement from version 1.0 to 1.1: add available_date field to marker
-if 'chimere.rss' in settings.INSTALLED_APPS:
- for cls in (Marker,):
- table = cls._meta.db_table
- query = QUERY_CHECK_FIELD % (table, 'available_date')
- cursor.execute(query)
- transaction.commit_unless_managed()
-
- row = cursor.fetchone()
- if not row:
- query_update = "ALTER TABLE " + table + " ADD COLUMN \
-available_date timestamp with time zone"
- cursor.execute(query_update)
- transaction.commit_unless_managed()
- print " * available_date added to table " + table + "."
-
-# changement from version 1.0 to 1.1: version of django 1.2
-# create specific height and width for image fields
-
-for cls, attr in ((Icon, "image"), (Marker, "picture"),
- (Route, "picture")):
- table = cls._meta.db_table
- query = QUERY_CHECK_FIELD % (table, 'width')
- cursor.execute(query)
- transaction.commit_unless_managed()
-
- row = cursor.fetchone()
- if not row:
- query_update = "ALTER TABLE "+table+" ADD COLUMN width integer"
- cursor.execute(query_update)
- transaction.commit_unless_managed()
- query_update = "ALTER TABLE "+table+" ADD COLUMN height integer"
- cursor.execute(query_update)
- transaction.commit_unless_managed()
- for obj in cls.objects.all():
- image = getattr(obj, attr)
- if not image:
- continue
- obj.width = image.width
- obj.height = image.height
- obj.save()
- print " * height and width of " + table + " corrected"
-
-# changement from version 1.0 to 1.1: multiple selection of categories
-
-for cls in (Marker, Route):
- table = cls._meta.db_table[len("main_"):]
- query = QUERY_CHECK_TABLE % ('main_' + table + '_categories')
- cursor.execute(query)
- transaction.commit_unless_managed()
-
- row = cursor.fetchone()
- if row:
- continue
- query_create = """
-CREATE TABLE "main_%s_categories" (
- "id" serial NOT NULL PRIMARY KEY,
- "%s_id" integer NOT NULL REFERENCES "main_%s" ("id") DEFERRABLE INITIALLY DEFERRED,
- "subcategory_id" integer NOT NULL REFERENCES "main_subcategory" ("id") DEFERRABLE INITIALLY DEFERRED,
- UNIQUE ("%s_id", "subcategory_id"));
-""" % (table, table, table, table)
- cursor.execute(query_create)
- transaction.commit_unless_managed()
- for obj in cls.objects.all():
- query = "select subcategory_id from main_%s where id=%d" % (table,
- obj.id)
- cursor.execute(query)
- transaction.commit_unless_managed()
-
- row = cursor.fetchone()
- if row:
- obj.categories.add(SubCategory.objects.get(id=row[0]))
- obj.save()
- query = "ALTER TABLE main_%s DROP COLUMN subcategory_id;" % table
- cursor.execute(query)
- transaction.commit_unless_managed()
- print " * main_%s_categories created" % table
-
-# -> version 1.2: associate point to route (for the future)
-query = QUERY_CHECK_FIELD % ('main_marker', 'route_id')
-cursor.execute(query)
-transaction.commit_unless_managed()
-
-row = cursor.fetchone()
-if not row:
- query_update = 'ALTER TABLE "main_marker" ADD COLUMN \
-"route_id" integer REFERENCES "main_route" ("id") DEFERRABLE INITIALLY DEFERRED'
- cursor.execute(query_update)
- transaction.commit_unless_managed()
- print " * route_id added to table main_marker."
-
-# -> version 1.3: file associated to routes
-query = QUERY_CHECK_TABLE % 'main_routefile'
-cursor.execute(query)
-transaction.commit_unless_managed()
-
-row = cursor.fetchone()
-if not row:
- query_create = """
- CREATE TABLE "main_routefile" (
- "id" serial NOT NULL PRIMARY KEY,
- "name" varchar(150) NOT NULL,
- "raw_file" varchar(100) NOT NULL,
- "simplified_file" varchar(100),
- "file_type" varchar(1) NOT NULL
- )
- ;
- ALTER TABLE "main_route" ADD COLUMN
- "associated_file_id" integer REFERENCES "main_routefile" ("id")
- DEFERRABLE INITIALLY DEFERRED;
- """
- cursor.execute(query_create)
- transaction.commit_unless_managed()
- print " * main_routefile created"
-
-# early versions before 0.1: save route with wrong SRID
-# only errors with default SRID is managed adapt the script for your SRID
-
-changed = False
-routes = Route.objects.all()
-for route in routes:
- # only one test: assume each point as been save with the same SRID...
- if route.route and route.route.srid == 4326 and \
- route.route[0][0] > 90 or route.route[0][0] < -90:
- changed = True
- new_route = []
- for pt in route.route:
- pt = proj.TransformPoint(pt[0], pt[1])
- new_route.append((pt[0], pt[1]))
- route.route = LineString(new_route)
- route.save()
-if changed:
- print " * projections of routes corrected"
diff --git a/chimere/static/chimere/css/styles.css b/chimere/static/chimere/css/styles.css
index 9d7ec17..6986189 100644
--- a/chimere/static/chimere/css/styles.css
+++ b/chimere/static/chimere/css/styles.css
@@ -53,7 +53,7 @@ body, h2, h3, th,
fieldset, .action li, #content,
#layer_selection #layer_list,
-#map-footer, #panel, #chimere_itinerary_panel, #areas,
+#map-footer, #panel, #chimere_itinerary_panel, #maps,
#welcome, #detail, .detail_footer a,
#content .olControlLayerSwitcher .layersDiv,
#content .olControlLayerSwitcher span,
@@ -76,7 +76,7 @@ div.warning, .errorlist{
#layer_selection h4,
#layer_selection #layer_list,
-#areas, #detail, #main-map,
+#maps, #detail, #main-map,
div.warning,
#content,
.action li.selected,
@@ -95,7 +95,7 @@ div.warning,
/* entête */
/*
-#areas h2, #panel h2{
+#maps h2, #panel h2{
-moz-border-radius: 4px 4px 0 0;
-webkit-border-radius: 4px 4px 0 0;
border-radius: 4px 4px 0 0;
@@ -109,7 +109,7 @@ h2, #action li, .detail_footer{
}
fieldset, #content, #panel,
-#areas, #welcome, #detail,
+#maps, #welcome, #detail,
#category_description, div.warning{
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
@@ -269,18 +269,18 @@ ul#action-2 {
width:100%;
}
-#areas-div,
-#areas-div label,
+#maps-div,
+#maps-div label,
#utils-div a{
padding:0.3em;
}
-#areas{
+#maps{
position:absolute;
z-index:5;
}
-#areas{
+#maps{
bottom:105px;
left:18px;
padding:0;
@@ -289,12 +289,12 @@ ul#action-2 {
overflow:auto;
}
-#areas ul{
+#maps ul{
margin:0;
padding:0 10px;
}
-#areas li{
+#maps li{
list-style:none;
}
diff --git a/chimere/static/chimere/js/base.js b/chimere/static/chimere/js/base.js
index fe8d954..73851a6 100644
--- a/chimere/static/chimere/js/base.js
+++ b/chimere/static/chimere/js/base.js
@@ -17,8 +17,8 @@ along with this program. If not, see .
See the file COPYING for details.
*/
-var default_area;
-var area_name;
+var default_map;
+var map_name;
/* indexOf definition for old IE versions */
if(!Array.indexOf){
@@ -65,7 +65,7 @@ function saveExtent() {
/* save the current extent in a cookie */
if(!map) return;
var extent_key = 'MAP_EXTENT';
- if (area_name){ extent_key = extent_key + '_' + area_name; }
+ if (map_name){ extent_key = extent_key + '_' + map_name; }
var extent = map.getExtent().transform(map.getProjectionObject(),
epsg_display_projection);
document.cookie = extent_key + "=" + extent.toArray().join('_')
@@ -77,7 +77,7 @@ function getExtent() {
var cookies = document.cookie.split(';');
var map_extent;
var extent_key = 'MAP_EXTENT';
- if (area_name){ extent_key = extent_key + '_' + area_name; }
+ if (map_name){ extent_key = extent_key + '_' + map_name; }
for (var i=0; i < cookies.length; i++){
var items = cookies[i].split('=');
key = items[0].split(' ').join('');
@@ -96,9 +96,9 @@ function zoomToCurrentExtent(map){
extent = new OpenLayers.Bounds(current_extent[0], current_extent[1],
current_extent[2], current_extent[3]);
}
- else if (OpenLayers && default_area && default_area.length == 4){
- extent = new OpenLayers.Bounds(default_area[0], default_area[1],
- default_area[2], default_area[3]);
+ else if (OpenLayers && default_map && default_map.length == 4){
+ extent = new OpenLayers.Bounds(default_map[0], default_map[1],
+ default_map[2], default_map[3]);
}
else{
return;
diff --git a/chimere/static/chimere/js/jquery.chimere-leaflet.js b/chimere/static/chimere/js/jquery.chimere-leaflet.js
index c67a7a1..f364bc5 100644
--- a/chimere/static/chimere/js/jquery.chimere-leaflet.js
+++ b/chimere/static/chimere/js/jquery.chimere-leaflet.js
@@ -47,7 +47,7 @@ See the file COPYING for details.
display_submited: false,
display_feature: null,
display_route: null,
- area_id: null,
+ map_id: null,
checked_categories: [],
zoom: null,
lat: null,
@@ -279,7 +279,7 @@ See the file COPYING for details.
current_extent = current_extent.replace(/\./g, 'D');
current_extent = current_extent.replace(/-/g, 'M');
var uri = extra_url
- if (settings.area_id) uri += settings.area_id + "/";
+ if (settings.map_id) uri += settings.map_id + "/";
uri += "getAvailableCategories/";
var params = {"current_extent": current_extent}
if (settings.display_submited) params["status"] = "A_S";
diff --git a/chimere/static/chimere/js/jquery.chimere-ol.js b/chimere/static/chimere/js/jquery.chimere-ol.js
index 6660b2e..ff07132 100644
--- a/chimere/static/chimere/js/jquery.chimere-ol.js
+++ b/chimere/static/chimere/js/jquery.chimere-ol.js
@@ -64,7 +64,7 @@ if (typeof(OpenLayers) != 'undefined'){
display_submited: false,
display_feature: null,
display_route: null,
- area_id: null,
+ map_id: null,
checked_categories: [],
zoom: null,
lat: null,
@@ -611,7 +611,7 @@ if (typeof(OpenLayers) != 'undefined'){
current_extent = current_extent.replace(/\./g, 'D');
current_extent = current_extent.replace(/-/g, 'M');
var uri = extra_url
- if (settings.area_id) uri += settings.area_id + "/";
+ if (settings.map_id) uri += settings.map_id + "/";
uri += "getAvailableCategories/";
var params = {"current_extent": current_extent}
if (settings.display_submited) params["status"] = "A_S";
@@ -1277,7 +1277,7 @@ if (typeof(OpenLayers) != 'undefined'){
* update current detail panel with an AJAX request
*/
var uri = extra_url
- if (settings.area_id) uri += settings.area_id + "/"
+ if (settings.map_id) uri += settings.map_id + "/"
uri += "getDetail/" + pk;
var params = {}
if (settings.simple) { params["simple"] = 1; }
diff --git a/chimere/static/leaflet/leaflet-src.js b/chimere/static/leaflet/leaflet-src.js
new file mode 100644
index 0000000..13f82bb
--- /dev/null
+++ b/chimere/static/leaflet/leaflet-src.js
@@ -0,0 +1,8338 @@
+/*
+ Leaflet, a JavaScript library for mobile-friendly interactive maps. http://leafletjs.com
+ (c) 2010-2013, Vladimir Agafonkin, CloudMade
+*/
+(function (window, document, undefined) {/*
+ * The L namespace contains all Leaflet classes and functions.
+ * This code allows you to handle any possible namespace conflicts.
+ */
+
+var L, originalL;
+
+if (typeof exports !== undefined + '') {
+ L = exports;
+} else {
+ originalL = window.L;
+ L = {};
+
+ L.noConflict = function () {
+ window.L = originalL;
+ return this;
+ };
+
+ window.L = L;
+}
+
+L.version = '0.5.1';
+
+
+/*
+ * L.Util contains various utility functions used throughout Leaflet code.
+ */
+
+L.Util = {
+ extend: function (dest) { // (Object[, Object, ...]) ->
+ var sources = Array.prototype.slice.call(arguments, 1),
+ i, j, len, src;
+
+ for (j = 0, len = sources.length; j < len; j++) {
+ src = sources[j] || {};
+ for (i in src) {
+ if (src.hasOwnProperty(i)) {
+ dest[i] = src[i];
+ }
+ }
+ }
+ return dest;
+ },
+
+ bind: function (fn, obj) { // (Function, Object) -> Function
+ var args = arguments.length > 2 ? Array.prototype.slice.call(arguments, 2) : null;
+ return function () {
+ return fn.apply(obj, args || arguments);
+ };
+ },
+
+ stamp: (function () {
+ var lastId = 0, key = '_leaflet_id';
+ return function (/*Object*/ obj) {
+ obj[key] = obj[key] || ++lastId;
+ return obj[key];
+ };
+ }()),
+
+ limitExecByInterval: function (fn, time, context) {
+ var lock, execOnUnlock;
+
+ return function wrapperFn() {
+ var args = arguments;
+
+ if (lock) {
+ execOnUnlock = true;
+ return;
+ }
+
+ lock = true;
+
+ setTimeout(function () {
+ lock = false;
+
+ if (execOnUnlock) {
+ wrapperFn.apply(context, args);
+ execOnUnlock = false;
+ }
+ }, time);
+
+ fn.apply(context, args);
+ };
+ },
+
+ falseFn: function () {
+ return false;
+ },
+
+ formatNum: function (num, digits) {
+ var pow = Math.pow(10, digits || 5);
+ return Math.round(num * pow) / pow;
+ },
+
+ splitWords: function (str) {
+ return str.replace(/^\s+|\s+$/g, '').split(/\s+/);
+ },
+
+ setOptions: function (obj, options) {
+ obj.options = L.extend({}, obj.options, options);
+ return obj.options;
+ },
+
+ getParamString: function (obj, existingUrl) {
+ var params = [];
+ for (var i in obj) {
+ if (obj.hasOwnProperty(i)) {
+ params.push(i + '=' + obj[i]);
+ }
+ }
+ return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&');
+ },
+
+ template: function (str, data) {
+ return str.replace(/\{ *([\w_]+) *\}/g, function (str, key) {
+ var value = data[key];
+ if (!data.hasOwnProperty(key)) {
+ throw new Error('No value provided for variable ' + str);
+ }
+ return value;
+ });
+ },
+
+ isArray: function (obj) {
+ return (Object.prototype.toString.call(obj) === '[object Array]');
+ },
+
+ emptyImageUrl: 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='
+};
+
+(function () {
+
+ // inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/
+
+ function getPrefixed(name) {
+ var i, fn,
+ prefixes = ['webkit', 'moz', 'o', 'ms'];
+
+ for (i = 0; i < prefixes.length && !fn; i++) {
+ fn = window[prefixes[i] + name];
+ }
+
+ return fn;
+ }
+
+ var lastTime = 0;
+
+ function timeoutDefer(fn) {
+ var time = +new Date(),
+ timeToCall = Math.max(0, 16 - (time - lastTime));
+
+ lastTime = time + timeToCall;
+ return window.setTimeout(fn, timeToCall);
+ }
+
+ var requestFn = window.requestAnimationFrame ||
+ getPrefixed('RequestAnimationFrame') || timeoutDefer;
+
+ var cancelFn = window.cancelAnimationFrame ||
+ getPrefixed('CancelAnimationFrame') ||
+ getPrefixed('CancelRequestAnimationFrame') ||
+ function (id) { window.clearTimeout(id); };
+
+
+ L.Util.requestAnimFrame = function (fn, context, immediate, element) {
+ fn = L.bind(fn, context);
+
+ if (immediate && requestFn === timeoutDefer) {
+ fn();
+ } else {
+ return requestFn.call(window, fn, element);
+ }
+ };
+
+ L.Util.cancelAnimFrame = function (id) {
+ if (id) {
+ cancelFn.call(window, id);
+ }
+ };
+
+}());
+
+// shortcuts for most used utility functions
+L.extend = L.Util.extend;
+L.bind = L.Util.bind;
+L.stamp = L.Util.stamp;
+L.setOptions = L.Util.setOptions;
+
+
+/*
+ * L.Class powers the OOP facilities of the library.
+ * Thanks to John Resig and Dean Edwards for inspiration!
+ */
+
+L.Class = function () {};
+
+L.Class.extend = function (props) {
+
+ // extended class with the new prototype
+ var NewClass = function () {
+
+ // call the constructor
+ if (this.initialize) {
+ this.initialize.apply(this, arguments);
+ }
+
+ // call all constructor hooks
+ if (this._initHooks) {
+ this.callInitHooks();
+ }
+ };
+
+ // instantiate class without calling constructor
+ var F = function () {};
+ F.prototype = this.prototype;
+
+ var proto = new F();
+ proto.constructor = NewClass;
+
+ NewClass.prototype = proto;
+
+ //inherit parent's statics
+ for (var i in this) {
+ if (this.hasOwnProperty(i) && i !== 'prototype') {
+ NewClass[i] = this[i];
+ }
+ }
+
+ // mix static properties into the class
+ if (props.statics) {
+ L.extend(NewClass, props.statics);
+ delete props.statics;
+ }
+
+ // mix includes into the prototype
+ if (props.includes) {
+ L.Util.extend.apply(null, [proto].concat(props.includes));
+ delete props.includes;
+ }
+
+ // merge options
+ if (props.options && proto.options) {
+ props.options = L.extend({}, proto.options, props.options);
+ }
+
+ // mix given properties into the prototype
+ L.extend(proto, props);
+
+ proto._initHooks = [];
+
+ var parent = this;
+ // add method for calling all hooks
+ proto.callInitHooks = function () {
+
+ if (this._initHooksCalled) { return; }
+
+ if (parent.prototype.callInitHooks) {
+ parent.prototype.callInitHooks.call(this);
+ }
+
+ this._initHooksCalled = true;
+
+ for (var i = 0, len = proto._initHooks.length; i < len; i++) {
+ proto._initHooks[i].call(this);
+ }
+ };
+
+ return NewClass;
+};
+
+
+// method for adding properties to prototype
+L.Class.include = function (props) {
+ L.extend(this.prototype, props);
+};
+
+// merge new default options to the Class
+L.Class.mergeOptions = function (options) {
+ L.extend(this.prototype.options, options);
+};
+
+// add a constructor hook
+L.Class.addInitHook = function (fn) { // (Function) || (String, args...)
+ var args = Array.prototype.slice.call(arguments, 1);
+
+ var init = typeof fn === 'function' ? fn : function () {
+ this[fn].apply(this, args);
+ };
+
+ this.prototype._initHooks = this.prototype._initHooks || [];
+ this.prototype._initHooks.push(init);
+};
+
+
+/*
+ * L.Mixin.Events is used to add custom events functionality to Leaflet classes.
+ */
+
+var key = '_leaflet_events';
+
+L.Mixin = {};
+
+L.Mixin.Events = {
+
+ addEventListener: function (types, fn, context) { // (String, Function[, Object]) or (Object[, Object])
+ var events = this[key] = this[key] || {},
+ type, i, len;
+
+ // Types can be a map of types/handlers
+ if (typeof types === 'object') {
+ for (type in types) {
+ if (types.hasOwnProperty(type)) {
+ this.addEventListener(type, types[type], fn);
+ }
+ }
+
+ return this;
+ }
+
+ types = L.Util.splitWords(types);
+
+ for (i = 0, len = types.length; i < len; i++) {
+ events[types[i]] = events[types[i]] || [];
+ events[types[i]].push({
+ action: fn,
+ context: context || this
+ });
+ }
+
+ return this;
+ },
+
+ hasEventListeners: function (type) { // (String) -> Boolean
+ return (key in this) && (type in this[key]) && (this[key][type].length > 0);
+ },
+
+ removeEventListener: function (types, fn, context) { // (String[, Function, Object]) or (Object[, Object])
+ var events = this[key],
+ type, i, len, listeners, j;
+
+ if (typeof types === 'object') {
+ for (type in types) {
+ if (types.hasOwnProperty(type)) {
+ this.removeEventListener(type, types[type], fn);
+ }
+ }
+
+ return this;
+ }
+
+ types = L.Util.splitWords(types);
+
+ for (i = 0, len = types.length; i < len; i++) {
+
+ if (this.hasEventListeners(types[i])) {
+ listeners = events[types[i]];
+
+ for (j = listeners.length - 1; j >= 0; j--) {
+ if (
+ (!fn || listeners[j].action === fn) &&
+ (!context || (listeners[j].context === context))
+ ) {
+ listeners.splice(j, 1);
+ }
+ }
+ }
+ }
+
+ return this;
+ },
+
+ fireEvent: function (type, data) { // (String[, Object])
+ if (!this.hasEventListeners(type)) {
+ return this;
+ }
+
+ var event = L.extend({
+ type: type,
+ target: this
+ }, data);
+
+ var listeners = this[key][type].slice();
+
+ for (var i = 0, len = listeners.length; i < len; i++) {
+ listeners[i].action.call(listeners[i].context || this, event);
+ }
+
+ return this;
+ }
+};
+
+L.Mixin.Events.on = L.Mixin.Events.addEventListener;
+L.Mixin.Events.off = L.Mixin.Events.removeEventListener;
+L.Mixin.Events.fire = L.Mixin.Events.fireEvent;
+
+
+/*
+ * L.Browser handles different browser and feature detections for internal Leaflet use.
+ */
+
+(function () {
+
+ var ie = !!window.ActiveXObject,
+ ie6 = ie && !window.XMLHttpRequest,
+ ie7 = ie && !document.querySelector,
+
+ // terrible browser detection to work around Safari / iOS / Android browser bugs
+ ua = navigator.userAgent.toLowerCase(),
+ webkit = ua.indexOf('webkit') !== -1,
+ chrome = ua.indexOf('chrome') !== -1,
+ android = ua.indexOf('android') !== -1,
+ android23 = ua.search('android [23]') !== -1,
+
+ mobile = typeof orientation !== undefined + '',
+ msTouch = window.navigator && window.navigator.msPointerEnabled &&
+ window.navigator.msMaxTouchPoints,
+ retina = ('devicePixelRatio' in window && window.devicePixelRatio > 1) ||
+ ('matchMedia' in window && window.matchMedia('(min-resolution:144dpi)') &&
+ window.matchMedia('(min-resolution:144dpi)').matches),
+
+ doc = document.documentElement,
+ ie3d = ie && ('transition' in doc.style),
+ webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()),
+ gecko3d = 'MozPerspective' in doc.style,
+ opera3d = 'OTransition' in doc.style,
+ any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d || opera3d);
+
+
+ var touch = !window.L_NO_TOUCH && (function () {
+
+ var startName = 'ontouchstart';
+
+ // IE10+ (We simulate these into touch* events in L.DomEvent and L.DomEvent.MsTouch) or WebKit, etc.
+ if (msTouch || (startName in doc)) {
+ return true;
+ }
+
+ // Firefox/Gecko
+ var div = document.createElement('div'),
+ supported = false;
+
+ if (!div.setAttribute) {
+ return false;
+ }
+ div.setAttribute(startName, 'return;');
+
+ if (typeof div[startName] === 'function') {
+ supported = true;
+ }
+
+ div.removeAttribute(startName);
+ div = null;
+
+ return supported;
+ }());
+
+
+ L.Browser = {
+ ie: ie,
+ ie6: ie6,
+ ie7: ie7,
+ webkit: webkit,
+
+ android: android,
+ android23: android23,
+
+ chrome: chrome,
+
+ ie3d: ie3d,
+ webkit3d: webkit3d,
+ gecko3d: gecko3d,
+ opera3d: opera3d,
+ any3d: any3d,
+
+ mobile: mobile,
+ mobileWebkit: mobile && webkit,
+ mobileWebkit3d: mobile && webkit3d,
+ mobileOpera: mobile && window.opera,
+
+ touch: touch,
+ msTouch: msTouch,
+
+ retina: retina
+ };
+
+}());
+
+
+/*
+ * L.Point represents a point with x and y coordinates.
+ */
+
+L.Point = function (/*Number*/ x, /*Number*/ y, /*Boolean*/ round) {
+ this.x = (round ? Math.round(x) : x);
+ this.y = (round ? Math.round(y) : y);
+};
+
+L.Point.prototype = {
+
+ clone: function () {
+ return new L.Point(this.x, this.y);
+ },
+
+ // non-destructive, returns a new point
+ add: function (point) {
+ return this.clone()._add(L.point(point));
+ },
+
+ // destructive, used directly for performance in situations where it's safe to modify existing point
+ _add: function (point) {
+ this.x += point.x;
+ this.y += point.y;
+ return this;
+ },
+
+ subtract: function (point) {
+ return this.clone()._subtract(L.point(point));
+ },
+
+ _subtract: function (point) {
+ this.x -= point.x;
+ this.y -= point.y;
+ return this;
+ },
+
+ divideBy: function (num) {
+ return this.clone()._divideBy(num);
+ },
+
+ _divideBy: function (num) {
+ this.x /= num;
+ this.y /= num;
+ return this;
+ },
+
+ multiplyBy: function (num) {
+ return this.clone()._multiplyBy(num);
+ },
+
+ _multiplyBy: function (num) {
+ this.x *= num;
+ this.y *= num;
+ return this;
+ },
+
+ round: function () {
+ return this.clone()._round();
+ },
+
+ _round: function () {
+ this.x = Math.round(this.x);
+ this.y = Math.round(this.y);
+ return this;
+ },
+
+ floor: function () {
+ return this.clone()._floor();
+ },
+
+ _floor: function () {
+ this.x = Math.floor(this.x);
+ this.y = Math.floor(this.y);
+ return this;
+ },
+
+ distanceTo: function (point) {
+ point = L.point(point);
+
+ var x = point.x - this.x,
+ y = point.y - this.y;
+
+ return Math.sqrt(x * x + y * y);
+ },
+
+ equals: function (point) {
+ return point.x === this.x &&
+ point.y === this.y;
+ },
+
+ toString: function () {
+ return 'Point(' +
+ L.Util.formatNum(this.x) + ', ' +
+ L.Util.formatNum(this.y) + ')';
+ }
+};
+
+L.point = function (x, y, round) {
+ if (x instanceof L.Point) {
+ return x;
+ }
+ if (L.Util.isArray(x)) {
+ return new L.Point(x[0], x[1]);
+ }
+ if (isNaN(x)) {
+ return x;
+ }
+ return new L.Point(x, y, round);
+};
+
+
+/*
+ * L.Bounds represents a rectangular area on the screen in pixel coordinates.
+ */
+
+L.Bounds = function (a, b) { //(Point, Point) or Point[]
+ if (!a) { return; }
+
+ var points = b ? [a, b] : a;
+
+ for (var i = 0, len = points.length; i < len; i++) {
+ this.extend(points[i]);
+ }
+};
+
+L.Bounds.prototype = {
+ // extend the bounds to contain the given point
+ extend: function (point) { // (Point)
+ point = L.point(point);
+
+ if (!this.min && !this.max) {
+ this.min = point.clone();
+ this.max = point.clone();
+ } else {
+ this.min.x = Math.min(point.x, this.min.x);
+ this.max.x = Math.max(point.x, this.max.x);
+ this.min.y = Math.min(point.y, this.min.y);
+ this.max.y = Math.max(point.y, this.max.y);
+ }
+ return this;
+ },
+
+ getCenter: function (round) { // (Boolean) -> Point
+ return new L.Point(
+ (this.min.x + this.max.x) / 2,
+ (this.min.y + this.max.y) / 2, round);
+ },
+
+ getBottomLeft: function () { // -> Point
+ return new L.Point(this.min.x, this.max.y);
+ },
+
+ getTopRight: function () { // -> Point
+ return new L.Point(this.max.x, this.min.y);
+ },
+
+ getSize: function () {
+ return this.max.subtract(this.min);
+ },
+
+ contains: function (obj) { // (Bounds) or (Point) -> Boolean
+ var min, max;
+
+ if (typeof obj[0] === 'number' || obj instanceof L.Point) {
+ obj = L.point(obj);
+ } else {
+ obj = L.bounds(obj);
+ }
+
+ if (obj instanceof L.Bounds) {
+ min = obj.min;
+ max = obj.max;
+ } else {
+ min = max = obj;
+ }
+
+ return (min.x >= this.min.x) &&
+ (max.x <= this.max.x) &&
+ (min.y >= this.min.y) &&
+ (max.y <= this.max.y);
+ },
+
+ intersects: function (bounds) { // (Bounds) -> Boolean
+ bounds = L.bounds(bounds);
+
+ var min = this.min,
+ max = this.max,
+ min2 = bounds.min,
+ max2 = bounds.max,
+ xIntersects = (max2.x >= min.x) && (min2.x <= max.x),
+ yIntersects = (max2.y >= min.y) && (min2.y <= max.y);
+
+ return xIntersects && yIntersects;
+ },
+
+ isValid: function () {
+ return !!(this.min && this.max);
+ }
+};
+
+L.bounds = function (a, b) { // (Bounds) or (Point, Point) or (Point[])
+ if (!a || a instanceof L.Bounds) {
+ return a;
+ }
+ return new L.Bounds(a, b);
+};
+
+
+/*
+ * L.Transformation is an utility class to perform simple point transformations through a 2d-matrix.
+ */
+
+L.Transformation = function (a, b, c, d) {
+ this._a = a;
+ this._b = b;
+ this._c = c;
+ this._d = d;
+};
+
+L.Transformation.prototype = {
+ transform: function (point, scale) { // (Point, Number) -> Point
+ return this._transform(point.clone(), scale);
+ },
+
+ // destructive transform (faster)
+ _transform: function (point, scale) {
+ scale = scale || 1;
+ point.x = scale * (this._a * point.x + this._b);
+ point.y = scale * (this._c * point.y + this._d);
+ return point;
+ },
+
+ untransform: function (point, scale) {
+ scale = scale || 1;
+ return new L.Point(
+ (point.x / scale - this._b) / this._a,
+ (point.y / scale - this._d) / this._c);
+ }
+};
+
+
+/*
+ * L.DomUtil contains various utility functions for working with DOM.
+ */
+
+L.DomUtil = {
+ get: function (id) {
+ return (typeof id === 'string' ? document.getElementById(id) : id);
+ },
+
+ getStyle: function (el, style) {
+
+ var value = el.style[style];
+
+ if (!value && el.currentStyle) {
+ value = el.currentStyle[style];
+ }
+
+ if ((!value || value === 'auto') && document.defaultView) {
+ var css = document.defaultView.getComputedStyle(el, null);
+ value = css ? css[style] : null;
+ }
+
+ return value === 'auto' ? null : value;
+ },
+
+ getViewportOffset: function (element) {
+
+ var top = 0,
+ left = 0,
+ el = element,
+ docBody = document.body,
+ pos,
+ ie7 = L.Browser.ie7;
+
+ do {
+ top += el.offsetTop || 0;
+ left += el.offsetLeft || 0;
+
+ //add borders
+ top += parseInt(L.DomUtil.getStyle(el, "borderTopWidth"), 10) || 0;
+ left += parseInt(L.DomUtil.getStyle(el, "borderLeftWidth"), 10) || 0;
+
+ pos = L.DomUtil.getStyle(el, 'position');
+
+ if (el.offsetParent === docBody && pos === 'absolute') { break; }
+
+ if (pos === 'fixed') {
+ top += docBody.scrollTop || 0;
+ left += docBody.scrollLeft || 0;
+ break;
+ }
+ el = el.offsetParent;
+
+ } while (el);
+
+ el = element;
+
+ do {
+ if (el === docBody) { break; }
+
+ top -= el.scrollTop || 0;
+ left -= el.scrollLeft || 0;
+
+ // webkit (and ie <= 7) handles RTL scrollLeft different to everyone else
+ // https://code.google.com/p/closure-library/source/browse/trunk/closure/goog/style/bidi.js
+ if (!L.DomUtil.documentIsLtr() && (L.Browser.webkit || ie7)) {
+ left += el.scrollWidth - el.clientWidth;
+
+ // ie7 shows the scrollbar by default and provides clientWidth counting it, so we
+ // need to add it back in if it is visible; scrollbar is on the left as we are RTL
+ if (ie7 && L.DomUtil.getStyle(el, 'overflow-y') !== 'hidden' &&
+ L.DomUtil.getStyle(el, 'overflow') !== 'hidden') {
+ left += 17;
+ }
+ }
+
+ el = el.parentNode;
+ } while (el);
+
+ return new L.Point(left, top);
+ },
+
+ documentIsLtr: function () {
+ if (!L.DomUtil._docIsLtrCached) {
+ L.DomUtil._docIsLtrCached = true;
+ L.DomUtil._docIsLtr = L.DomUtil.getStyle(document.body, 'direction') === "ltr";
+ }
+ return L.DomUtil._docIsLtr;
+ },
+
+ create: function (tagName, className, container) {
+
+ var el = document.createElement(tagName);
+ el.className = className;
+
+ if (container) {
+ container.appendChild(el);
+ }
+
+ return el;
+ },
+
+ disableTextSelection: function () {
+ if (document.selection && document.selection.empty) {
+ document.selection.empty();
+ }
+ if (!this._onselectstart) {
+ this._onselectstart = document.onselectstart || null;
+ document.onselectstart = L.Util.falseFn;
+ }
+ },
+
+ enableTextSelection: function () {
+ if (document.onselectstart === L.Util.falseFn) {
+ document.onselectstart = this._onselectstart;
+ this._onselectstart = null;
+ }
+ },
+
+ hasClass: function (el, name) {
+ return (el.className.length > 0) &&
+ new RegExp("(^|\\s)" + name + "(\\s|$)").test(el.className);
+ },
+
+ addClass: function (el, name) {
+ if (!L.DomUtil.hasClass(el, name)) {
+ el.className += (el.className ? ' ' : '') + name;
+ }
+ },
+
+ removeClass: function (el, name) {
+
+ function replaceFn(w, match) {
+ if (match === name) { return ''; }
+ return w;
+ }
+
+ el.className = el.className
+ .replace(/(\S+)\s*/g, replaceFn)
+ .replace(/(^\s+|\s+$)/, '');
+ },
+
+ setOpacity: function (el, value) {
+
+ if ('opacity' in el.style) {
+ el.style.opacity = value;
+
+ } else if ('filter' in el.style) {
+
+ var filter = false,
+ filterName = 'DXImageTransform.Microsoft.Alpha';
+
+ // filters collection throws an error if we try to retrieve a filter that doesn't exist
+ try { filter = el.filters.item(filterName); } catch (e) {}
+
+ value = Math.round(value * 100);
+
+ if (filter) {
+ filter.Enabled = (value !== 100);
+ filter.Opacity = value;
+ } else {
+ el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')';
+ }
+ }
+ },
+
+ testProp: function (props) {
+
+ var style = document.documentElement.style;
+
+ for (var i = 0; i < props.length; i++) {
+ if (props[i] in style) {
+ return props[i];
+ }
+ }
+ return false;
+ },
+
+ getTranslateString: function (point) {
+ // on WebKit browsers (Chrome/Safari/iOS Safari/Android) using translate3d instead of translate
+ // makes animation smoother as it ensures HW accel is used. Firefox 13 doesn't care
+ // (same speed either way), Opera 12 doesn't support translate3d
+
+ var is3d = L.Browser.webkit3d,
+ open = 'translate' + (is3d ? '3d' : '') + '(',
+ close = (is3d ? ',0' : '') + ')';
+
+ return open + point.x + 'px,' + point.y + 'px' + close;
+ },
+
+ getScaleString: function (scale, origin) {
+
+ var preTranslateStr = L.DomUtil.getTranslateString(origin.add(origin.multiplyBy(-1 * scale))),
+ scaleStr = ' scale(' + scale + ') ';
+
+ return preTranslateStr + scaleStr;
+ },
+
+ setPosition: function (el, point, disable3D) { // (HTMLElement, Point[, Boolean])
+
+ el._leaflet_pos = point;
+
+ if (!disable3D && L.Browser.any3d) {
+ el.style[L.DomUtil.TRANSFORM] = L.DomUtil.getTranslateString(point);
+
+ // workaround for Android 2/3 stability (https://github.com/CloudMade/Leaflet/issues/69)
+ if (L.Browser.mobileWebkit3d) {
+ el.style.WebkitBackfaceVisibility = 'hidden';
+ }
+ } else {
+ el.style.left = point.x + 'px';
+ el.style.top = point.y + 'px';
+ }
+ },
+
+ getPosition: function (el) {
+ // this method is only used for elements previously positioned using setPosition,
+ // so it's safe to cache the position for performance
+ return el._leaflet_pos;
+ }
+};
+
+
+// prefix style property names
+
+L.DomUtil.TRANSFORM = L.DomUtil.testProp(
+ ['transform', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform']);
+
+// webkitTransition comes first because some browser versions that drop vendor prefix don't do
+// the same for the transitionend event, in particular the Android 4.1 stock browser
+
+L.DomUtil.TRANSITION = L.DomUtil.testProp(
+ ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);
+
+L.DomUtil.TRANSITION_END =
+ L.DomUtil.TRANSITION === 'webkitTransition' || L.DomUtil.TRANSITION === 'OTransition' ?
+ L.DomUtil.TRANSITION + 'End' : 'transitionend';
+
+
+/*
+ * L.LatLng represents a geographical point with latitude and longitude coordinates.
+ */
+
+L.LatLng = function (rawLat, rawLng) { // (Number, Number)
+ var lat = parseFloat(rawLat),
+ lng = parseFloat(rawLng);
+
+ if (isNaN(lat) || isNaN(lng)) {
+ throw new Error('Invalid LatLng object: (' + rawLat + ', ' + rawLng + ')');
+ }
+
+ this.lat = lat;
+ this.lng = lng;
+};
+
+L.extend(L.LatLng, {
+ DEG_TO_RAD: Math.PI / 180,
+ RAD_TO_DEG: 180 / Math.PI,
+ MAX_MARGIN: 1.0E-9 // max margin of error for the "equals" check
+});
+
+L.LatLng.prototype = {
+ equals: function (obj) { // (LatLng) -> Boolean
+ if (!obj) { return false; }
+
+ obj = L.latLng(obj);
+
+ var margin = Math.max(
+ Math.abs(this.lat - obj.lat),
+ Math.abs(this.lng - obj.lng));
+
+ return margin <= L.LatLng.MAX_MARGIN;
+ },
+
+ toString: function (precision) { // (Number) -> String
+ return 'LatLng(' +
+ L.Util.formatNum(this.lat, precision) + ', ' +
+ L.Util.formatNum(this.lng, precision) + ')';
+ },
+
+ // Haversine distance formula, see http://en.wikipedia.org/wiki/Haversine_formula
+ // TODO move to projection code, LatLng shouldn't know about Earth
+ distanceTo: function (other) { // (LatLng) -> Number
+ other = L.latLng(other);
+
+ var R = 6378137, // earth radius in meters
+ d2r = L.LatLng.DEG_TO_RAD,
+ dLat = (other.lat - this.lat) * d2r,
+ dLon = (other.lng - this.lng) * d2r,
+ lat1 = this.lat * d2r,
+ lat2 = other.lat * d2r,
+ sin1 = Math.sin(dLat / 2),
+ sin2 = Math.sin(dLon / 2);
+
+ var a = sin1 * sin1 + sin2 * sin2 * Math.cos(lat1) * Math.cos(lat2);
+
+ return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
+ },
+
+ wrap: function (a, b) { // (Number, Number) -> LatLng
+ var lng = this.lng;
+
+ a = a || -180;
+ b = b || 180;
+
+ lng = (lng + b) % (b - a) + (lng < a || lng === b ? b : a);
+
+ return new L.LatLng(this.lat, lng);
+ }
+};
+
+L.latLng = function (a, b) { // (LatLng) or ([Number, Number]) or (Number, Number)
+ if (a instanceof L.LatLng) {
+ return a;
+ }
+ if (L.Util.isArray(a)) {
+ return new L.LatLng(a[0], a[1]);
+ }
+ if (isNaN(a)) {
+ return a;
+ }
+ return new L.LatLng(a, b);
+};
+
+
+
+/*
+ * L.LatLngBounds represents a rectangular area on the map in geographical coordinates.
+ */
+
+L.LatLngBounds = function (southWest, northEast) { // (LatLng, LatLng) or (LatLng[])
+ if (!southWest) { return; }
+
+ var latlngs = northEast ? [southWest, northEast] : southWest;
+
+ for (var i = 0, len = latlngs.length; i < len; i++) {
+ this.extend(latlngs[i]);
+ }
+};
+
+L.LatLngBounds.prototype = {
+ // extend the bounds to contain the given point or bounds
+ extend: function (obj) { // (LatLng) or (LatLngBounds)
+ if (typeof obj[0] === 'number' || typeof obj[0] === 'string' || obj instanceof L.LatLng) {
+ obj = L.latLng(obj);
+ } else {
+ obj = L.latLngBounds(obj);
+ }
+
+ if (obj instanceof L.LatLng) {
+ if (!this._southWest && !this._northEast) {
+ this._southWest = new L.LatLng(obj.lat, obj.lng);
+ this._northEast = new L.LatLng(obj.lat, obj.lng);
+ } else {
+ this._southWest.lat = Math.min(obj.lat, this._southWest.lat);
+ this._southWest.lng = Math.min(obj.lng, this._southWest.lng);
+
+ this._northEast.lat = Math.max(obj.lat, this._northEast.lat);
+ this._northEast.lng = Math.max(obj.lng, this._northEast.lng);
+ }
+ } else if (obj instanceof L.LatLngBounds) {
+ this.extend(obj._southWest);
+ this.extend(obj._northEast);
+ }
+ return this;
+ },
+
+ // extend the bounds by a percentage
+ pad: function (bufferRatio) { // (Number) -> LatLngBounds
+ var sw = this._southWest,
+ ne = this._northEast,
+ heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio,
+ widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio;
+
+ return new L.LatLngBounds(
+ new L.LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer),
+ new L.LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer));
+ },
+
+ getCenter: function () { // -> LatLng
+ return new L.LatLng(
+ (this._southWest.lat + this._northEast.lat) / 2,
+ (this._southWest.lng + this._northEast.lng) / 2);
+ },
+
+ getSouthWest: function () {
+ return this._southWest;
+ },
+
+ getNorthEast: function () {
+ return this._northEast;
+ },
+
+ getNorthWest: function () {
+ return new L.LatLng(this._northEast.lat, this._southWest.lng);
+ },
+
+ getSouthEast: function () {
+ return new L.LatLng(this._southWest.lat, this._northEast.lng);
+ },
+
+ contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean
+ if (typeof obj[0] === 'number' || obj instanceof L.LatLng) {
+ obj = L.latLng(obj);
+ } else {
+ obj = L.latLngBounds(obj);
+ }
+
+ var sw = this._southWest,
+ ne = this._northEast,
+ sw2, ne2;
+
+ if (obj instanceof L.LatLngBounds) {
+ sw2 = obj.getSouthWest();
+ ne2 = obj.getNorthEast();
+ } else {
+ sw2 = ne2 = obj;
+ }
+
+ return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) &&
+ (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng);
+ },
+
+ intersects: function (bounds) { // (LatLngBounds)
+ bounds = L.latLngBounds(bounds);
+
+ var sw = this._southWest,
+ ne = this._northEast,
+ sw2 = bounds.getSouthWest(),
+ ne2 = bounds.getNorthEast(),
+
+ latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat),
+ lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng);
+
+ return latIntersects && lngIntersects;
+ },
+
+ toBBoxString: function () {
+ var sw = this._southWest,
+ ne = this._northEast;
+
+ return [sw.lng, sw.lat, ne.lng, ne.lat].join(',');
+ },
+
+ equals: function (bounds) { // (LatLngBounds)
+ if (!bounds) { return false; }
+
+ bounds = L.latLngBounds(bounds);
+
+ return this._southWest.equals(bounds.getSouthWest()) &&
+ this._northEast.equals(bounds.getNorthEast());
+ },
+
+ isValid: function () {
+ return !!(this._southWest && this._northEast);
+ }
+};
+
+//TODO International date line?
+
+L.latLngBounds = function (a, b) { // (LatLngBounds) or (LatLng, LatLng)
+ if (!a || a instanceof L.LatLngBounds) {
+ return a;
+ }
+ return new L.LatLngBounds(a, b);
+};
+
+
+/*
+ * L.Projection contains various geographical projections used by CRS classes.
+ */
+
+L.Projection = {};
+
+
+/*
+ * Spherical Mercator is the most popular map projection, used by EPSG:3857 CRS used by default.
+ */
+
+L.Projection.SphericalMercator = {
+ MAX_LATITUDE: 85.0511287798,
+
+ project: function (latlng) { // (LatLng) -> Point
+ var d = L.LatLng.DEG_TO_RAD,
+ max = this.MAX_LATITUDE,
+ lat = Math.max(Math.min(max, latlng.lat), -max),
+ x = latlng.lng * d,
+ y = lat * d;
+
+ y = Math.log(Math.tan((Math.PI / 4) + (y / 2)));
+
+ return new L.Point(x, y);
+ },
+
+ unproject: function (point) { // (Point, Boolean) -> LatLng
+ var d = L.LatLng.RAD_TO_DEG,
+ lng = point.x * d,
+ lat = (2 * Math.atan(Math.exp(point.y)) - (Math.PI / 2)) * d;
+
+ return new L.LatLng(lat, lng);
+ }
+};
+
+
+/*
+ * Simple equirectangular (Plate Carree) projection, used by CRS like EPSG:4326 and Simple.
+ */
+
+L.Projection.LonLat = {
+ project: function (latlng) {
+ return new L.Point(latlng.lng, latlng.lat);
+ },
+
+ unproject: function (point) {
+ return new L.LatLng(point.y, point.x);
+ }
+};
+
+
+/*
+ * L.CRS is a base object for all defined CRS (Coordinate Reference Systems) in Leaflet.
+ */
+
+L.CRS = {
+ latLngToPoint: function (latlng, zoom) { // (LatLng, Number) -> Point
+ var projectedPoint = this.projection.project(latlng),
+ scale = this.scale(zoom);
+
+ return this.transformation._transform(projectedPoint, scale);
+ },
+
+ pointToLatLng: function (point, zoom) { // (Point, Number[, Boolean]) -> LatLng
+ var scale = this.scale(zoom),
+ untransformedPoint = this.transformation.untransform(point, scale);
+
+ return this.projection.unproject(untransformedPoint);
+ },
+
+ project: function (latlng) {
+ return this.projection.project(latlng);
+ },
+
+ scale: function (zoom) {
+ return 256 * Math.pow(2, zoom);
+ }
+};
+
+
+/*
+ * A simple CRS that can be used for flat non-Earth maps like panoramas or game maps.
+ */
+
+L.CRS.Simple = L.extend({}, L.CRS, {
+ projection: L.Projection.LonLat,
+ transformation: new L.Transformation(1, 0, -1, 0),
+
+ scale: function (zoom) {
+ return Math.pow(2, zoom);
+ }
+});
+
+
+/*
+ * L.CRS.EPSG3857 (Spherical Mercator) is the most common CRS for web mapping
+ * and is used by Leaflet by default.
+ */
+
+L.CRS.EPSG3857 = L.extend({}, L.CRS, {
+ code: 'EPSG:3857',
+
+ projection: L.Projection.SphericalMercator,
+ transformation: new L.Transformation(0.5 / Math.PI, 0.5, -0.5 / Math.PI, 0.5),
+
+ project: function (latlng) { // (LatLng) -> Point
+ var projectedPoint = this.projection.project(latlng),
+ earthRadius = 6378137;
+ return projectedPoint.multiplyBy(earthRadius);
+ }
+});
+
+L.CRS.EPSG900913 = L.extend({}, L.CRS.EPSG3857, {
+ code: 'EPSG:900913'
+});
+
+
+/*
+ * L.CRS.EPSG4326 is a CRS popular among advanced GIS specialists.
+ */
+
+L.CRS.EPSG4326 = L.extend({}, L.CRS, {
+ code: 'EPSG:4326',
+
+ projection: L.Projection.LonLat,
+ transformation: new L.Transformation(1 / 360, 0.5, -1 / 360, 0.5)
+});
+
+
+/*
+ * L.Map is the central class of the API - it is used to create a map.
+ */
+
+L.Map = L.Class.extend({
+
+ includes: L.Mixin.Events,
+
+ options: {
+ crs: L.CRS.EPSG3857,
+
+ /*
+ center: LatLng,
+ zoom: Number,
+ layers: Array,
+ */
+
+ fadeAnimation: L.DomUtil.TRANSITION && !L.Browser.android23,
+ trackResize: true,
+ markerZoomAnimation: L.DomUtil.TRANSITION && L.Browser.any3d
+ },
+
+ initialize: function (id, options) { // (HTMLElement or String, Object)
+ options = L.setOptions(this, options);
+
+ this._initContainer(id);
+ this._initLayout();
+ this.callInitHooks();
+ this._initEvents();
+
+ if (options.maxBounds) {
+ this.setMaxBounds(options.maxBounds);
+ }
+
+ if (options.center && options.zoom !== undefined) {
+ this.setView(L.latLng(options.center), options.zoom, true);
+ }
+
+ this._initLayers(options.layers);
+ },
+
+
+ // public methods that modify map state
+
+ // replaced by animation-powered implementation in Map.PanAnimation.js
+ setView: function (center, zoom) {
+ this._resetView(L.latLng(center), this._limitZoom(zoom));
+ return this;
+ },
+
+ setZoom: function (zoom) { // (Number)
+ return this.setView(this.getCenter(), zoom);
+ },
+
+ zoomIn: function (delta) {
+ return this.setZoom(this._zoom + (delta || 1));
+ },
+
+ zoomOut: function (delta) {
+ return this.setZoom(this._zoom - (delta || 1));
+ },
+
+ fitBounds: function (bounds) { // (LatLngBounds)
+ var zoom = this.getBoundsZoom(bounds);
+ return this.setView(L.latLngBounds(bounds).getCenter(), zoom);
+ },
+
+ fitWorld: function () {
+ var sw = new L.LatLng(-60, -170),
+ ne = new L.LatLng(85, 179);
+
+ return this.fitBounds(new L.LatLngBounds(sw, ne));
+ },
+
+ panTo: function (center) { // (LatLng)
+ return this.setView(center, this._zoom);
+ },
+
+ panBy: function (offset) { // (Point)
+ // replaced with animated panBy in Map.Animation.js
+ this.fire('movestart');
+
+ this._rawPanBy(L.point(offset));
+
+ this.fire('move');
+ return this.fire('moveend');
+ },
+
+ setMaxBounds: function (bounds) {
+ bounds = L.latLngBounds(bounds);
+
+ this.options.maxBounds = bounds;
+
+ if (!bounds) {
+ this._boundsMinZoom = null;
+ return this;
+ }
+
+ var minZoom = this.getBoundsZoom(bounds, true);
+
+ this._boundsMinZoom = minZoom;
+
+ if (this._loaded) {
+ if (this._zoom < minZoom) {
+ this.setView(bounds.getCenter(), minZoom);
+ } else {
+ this.panInsideBounds(bounds);
+ }
+ }
+
+ return this;
+ },
+
+ panInsideBounds: function (bounds) {
+ bounds = L.latLngBounds(bounds);
+
+ var viewBounds = this.getBounds(),
+ viewSw = this.project(viewBounds.getSouthWest()),
+ viewNe = this.project(viewBounds.getNorthEast()),
+ sw = this.project(bounds.getSouthWest()),
+ ne = this.project(bounds.getNorthEast()),
+ dx = 0,
+ dy = 0;
+
+ if (viewNe.y < ne.y) { // north
+ dy = ne.y - viewNe.y;
+ }
+ if (viewNe.x > ne.x) { // east
+ dx = ne.x - viewNe.x;
+ }
+ if (viewSw.y > sw.y) { // south
+ dy = sw.y - viewSw.y;
+ }
+ if (viewSw.x < sw.x) { // west
+ dx = sw.x - viewSw.x;
+ }
+
+ return this.panBy(new L.Point(dx, dy, true));
+ },
+
+ addLayer: function (layer) {
+ // TODO method is too big, refactor
+
+ var id = L.stamp(layer);
+
+ if (this._layers[id]) { return this; }
+
+ this._layers[id] = layer;
+
+ // TODO getMaxZoom, getMinZoom in ILayer (instead of options)
+ if (layer.options && (!isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom))) {
+ this._zoomBoundLayers[id] = layer;
+ this._updateZoomLevels();
+ }
+
+ // TODO looks ugly, refactor!!!
+ if (this.options.zoomAnimation && L.TileLayer && (layer instanceof L.TileLayer)) {
+ this._tileLayersNum++;
+ this._tileLayersToLoad++;
+ layer.on('load', this._onTileLayerLoad, this);
+ }
+
+ this.whenReady(function () {
+ layer.onAdd(this);
+ this.fire('layeradd', {layer: layer});
+ }, this);
+
+ return this;
+ },
+
+ removeLayer: function (layer) {
+ var id = L.stamp(layer);
+
+ if (!this._layers[id]) { return; }
+
+ layer.onRemove(this);
+
+ delete this._layers[id];
+ if (this._zoomBoundLayers[id]) {
+ delete this._zoomBoundLayers[id];
+ this._updateZoomLevels();
+ }
+
+ // TODO looks ugly, refactor
+ if (this.options.zoomAnimation && L.TileLayer && (layer instanceof L.TileLayer)) {
+ this._tileLayersNum--;
+ this._tileLayersToLoad--;
+ layer.off('load', this._onTileLayerLoad, this);
+ }
+
+ return this.fire('layerremove', {layer: layer});
+ },
+
+ hasLayer: function (layer) {
+ var id = L.stamp(layer);
+ return this._layers.hasOwnProperty(id);
+ },
+
+ invalidateSize: function (animate) {
+ var oldSize = this.getSize();
+
+ this._sizeChanged = true;
+
+ if (this.options.maxBounds) {
+ this.setMaxBounds(this.options.maxBounds);
+ }
+
+ if (!this._loaded) { return this; }
+
+ var offset = oldSize._subtract(this.getSize())._divideBy(2)._round();
+
+ if (animate === true) {
+ this.panBy(offset);
+ } else {
+ this._rawPanBy(offset);
+
+ this.fire('move');
+
+ clearTimeout(this._sizeTimer);
+ this._sizeTimer = setTimeout(L.bind(this.fire, this, 'moveend'), 200);
+ }
+ return this;
+ },
+
+ // TODO handler.addTo
+ addHandler: function (name, HandlerClass) {
+ if (!HandlerClass) { return; }
+
+ this[name] = new HandlerClass(this);
+
+ if (this.options[name]) {
+ this[name].enable();
+ }
+
+ return this;
+ },
+
+
+ // public methods for getting map state
+
+ getCenter: function () { // (Boolean) -> LatLng
+ return this.layerPointToLatLng(this._getCenterLayerPoint());
+ },
+
+ getZoom: function () {
+ return this._zoom;
+ },
+
+ getBounds: function () {
+ var bounds = this.getPixelBounds(),
+ sw = this.unproject(bounds.getBottomLeft()),
+ ne = this.unproject(bounds.getTopRight());
+
+ return new L.LatLngBounds(sw, ne);
+ },
+
+ getMinZoom: function () {
+ var z1 = this.options.minZoom || 0,
+ z2 = this._layersMinZoom || 0,
+ z3 = this._boundsMinZoom || 0;
+
+ return Math.max(z1, z2, z3);
+ },
+
+ getMaxZoom: function () {
+ var z1 = this.options.maxZoom === undefined ? Infinity : this.options.maxZoom,
+ z2 = this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom;
+
+ return Math.min(z1, z2);
+ },
+
+ getBoundsZoom: function (bounds, inside) { // (LatLngBounds, Boolean) -> Number
+ bounds = L.latLngBounds(bounds);
+
+ var size = this.getSize(),
+ zoom = this.options.minZoom || 0,
+ maxZoom = this.getMaxZoom(),
+ ne = bounds.getNorthEast(),
+ sw = bounds.getSouthWest(),
+ boundsSize,
+ nePoint,
+ swPoint,
+ zoomNotFound = true;
+
+ if (inside) {
+ zoom--;
+ }
+
+ do {
+ zoom++;
+ nePoint = this.project(ne, zoom);
+ swPoint = this.project(sw, zoom);
+
+ boundsSize = new L.Point(
+ Math.abs(nePoint.x - swPoint.x),
+ Math.abs(swPoint.y - nePoint.y));
+
+ if (!inside) {
+ zoomNotFound = boundsSize.x <= size.x && boundsSize.y <= size.y;
+ } else {
+ zoomNotFound = boundsSize.x < size.x || boundsSize.y < size.y;
+ }
+ } while (zoomNotFound && zoom <= maxZoom);
+
+ if (zoomNotFound && inside) {
+ return null;
+ }
+
+ return inside ? zoom : zoom - 1;
+ },
+
+ getSize: function () {
+ if (!this._size || this._sizeChanged) {
+ this._size = new L.Point(
+ this._container.clientWidth,
+ this._container.clientHeight);
+
+ this._sizeChanged = false;
+ }
+ return this._size.clone();
+ },
+
+ getPixelBounds: function () {
+ var topLeftPoint = this._getTopLeftPoint();
+ return new L.Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
+ },
+
+ getPixelOrigin: function () {
+ return this._initialTopLeftPoint;
+ },
+
+ getPanes: function () {
+ return this._panes;
+ },
+
+ getContainer: function () {
+ return this._container;
+ },
+
+
+ // TODO replace with universal implementation after refactoring projections
+
+ getZoomScale: function (toZoom) {
+ var crs = this.options.crs;
+ return crs.scale(toZoom) / crs.scale(this._zoom);
+ },
+
+ getScaleZoom: function (scale) {
+ return this._zoom + (Math.log(scale) / Math.LN2);
+ },
+
+
+ // conversion methods
+
+ project: function (latlng, zoom) { // (LatLng[, Number]) -> Point
+ zoom = zoom === undefined ? this._zoom : zoom;
+ return this.options.crs.latLngToPoint(L.latLng(latlng), zoom);
+ },
+
+ unproject: function (point, zoom) { // (Point[, Number]) -> LatLng
+ zoom = zoom === undefined ? this._zoom : zoom;
+ return this.options.crs.pointToLatLng(L.point(point), zoom);
+ },
+
+ layerPointToLatLng: function (point) { // (Point)
+ var projectedPoint = L.point(point).add(this._initialTopLeftPoint);
+ return this.unproject(projectedPoint);
+ },
+
+ latLngToLayerPoint: function (latlng) { // (LatLng)
+ var projectedPoint = this.project(L.latLng(latlng))._round();
+ return projectedPoint._subtract(this._initialTopLeftPoint);
+ },
+
+ containerPointToLayerPoint: function (point) { // (Point)
+ return L.point(point).subtract(this._getMapPanePos());
+ },
+
+ layerPointToContainerPoint: function (point) { // (Point)
+ return L.point(point).add(this._getMapPanePos());
+ },
+
+ containerPointToLatLng: function (point) {
+ var layerPoint = this.containerPointToLayerPoint(L.point(point));
+ return this.layerPointToLatLng(layerPoint);
+ },
+
+ latLngToContainerPoint: function (latlng) {
+ return this.layerPointToContainerPoint(this.latLngToLayerPoint(L.latLng(latlng)));
+ },
+
+ mouseEventToContainerPoint: function (e) { // (MouseEvent)
+ return L.DomEvent.getMousePosition(e, this._container);
+ },
+
+ mouseEventToLayerPoint: function (e) { // (MouseEvent)
+ return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e));
+ },
+
+ mouseEventToLatLng: function (e) { // (MouseEvent)
+ return this.layerPointToLatLng(this.mouseEventToLayerPoint(e));
+ },
+
+
+ // map initialization methods
+
+ _initContainer: function (id) {
+ var container = this._container = L.DomUtil.get(id);
+ if (container._leaflet) {
+ throw new Error("Map container is already initialized.");
+ }
+
+ container._leaflet = true;
+ },
+
+ _initLayout: function () {
+ var container = this._container;
+
+ L.DomUtil.addClass(container, 'leaflet-container');
+
+ if (L.Browser.touch) {
+ L.DomUtil.addClass(container, 'leaflet-touch');
+ }
+
+ if (this.options.fadeAnimation) {
+ L.DomUtil.addClass(container, 'leaflet-fade-anim');
+ }
+
+ var position = L.DomUtil.getStyle(container, 'position');
+
+ if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') {
+ container.style.position = 'relative';
+ }
+
+ this._initPanes();
+
+ if (this._initControlPos) {
+ this._initControlPos();
+ }
+ },
+
+ _initPanes: function () {
+ var panes = this._panes = {};
+
+ this._mapPane = panes.mapPane = this._createPane('leaflet-map-pane', this._container);
+
+ this._tilePane = panes.tilePane = this._createPane('leaflet-tile-pane', this._mapPane);
+ panes.objectsPane = this._createPane('leaflet-objects-pane', this._mapPane);
+ panes.shadowPane = this._createPane('leaflet-shadow-pane');
+ panes.overlayPane = this._createPane('leaflet-overlay-pane');
+ panes.markerPane = this._createPane('leaflet-marker-pane');
+ panes.popupPane = this._createPane('leaflet-popup-pane');
+
+ var zoomHide = ' leaflet-zoom-hide';
+
+ if (!this.options.markerZoomAnimation) {
+ L.DomUtil.addClass(panes.markerPane, zoomHide);
+ L.DomUtil.addClass(panes.shadowPane, zoomHide);
+ L.DomUtil.addClass(panes.popupPane, zoomHide);
+ }
+ },
+
+ _createPane: function (className, container) {
+ return L.DomUtil.create('div', className, container || this._panes.objectsPane);
+ },
+
+ _initLayers: function (layers) {
+ layers = layers ? (L.Util.isArray(layers) ? layers : [layers]) : [];
+
+ this._layers = {};
+ this._zoomBoundLayers = {};
+ this._tileLayersNum = 0;
+
+ var i, len;
+
+ for (i = 0, len = layers.length; i < len; i++) {
+ this.addLayer(layers[i]);
+ }
+ },
+
+
+ // private methods that modify map state
+
+ _resetView: function (center, zoom, preserveMapOffset, afterZoomAnim) {
+
+ var zoomChanged = (this._zoom !== zoom);
+
+ if (!afterZoomAnim) {
+ this.fire('movestart');
+
+ if (zoomChanged) {
+ this.fire('zoomstart');
+ }
+ }
+
+ this._zoom = zoom;
+
+ this._initialTopLeftPoint = this._getNewTopLeftPoint(center);
+
+ if (!preserveMapOffset) {
+ L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0));
+ } else {
+ this._initialTopLeftPoint._add(this._getMapPanePos());
+ }
+
+ this._tileLayersToLoad = this._tileLayersNum;
+
+ var loading = !this._loaded;
+ this._loaded = true;
+
+ this.fire('viewreset', {hard: !preserveMapOffset});
+
+ this.fire('move');
+
+ if (zoomChanged || afterZoomAnim) {
+ this.fire('zoomend');
+ }
+
+ this.fire('moveend', {hard: !preserveMapOffset});
+
+ if (loading) {
+ this.fire('load');
+ }
+ },
+
+ _rawPanBy: function (offset) {
+ L.DomUtil.setPosition(this._mapPane, this._getMapPanePos().subtract(offset));
+ },
+
+ _updateZoomLevels: function () {
+ var i,
+ minZoom = Infinity,
+ maxZoom = -Infinity;
+
+ for (i in this._zoomBoundLayers) {
+ if (this._zoomBoundLayers.hasOwnProperty(i)) {
+ var layer = this._zoomBoundLayers[i];
+ if (!isNaN(layer.options.minZoom)) {
+ minZoom = Math.min(minZoom, layer.options.minZoom);
+ }
+ if (!isNaN(layer.options.maxZoom)) {
+ maxZoom = Math.max(maxZoom, layer.options.maxZoom);
+ }
+ }
+ }
+
+ if (i === undefined) { // we have no tilelayers
+ this._layersMaxZoom = this._layersMinZoom = undefined;
+ } else {
+ this._layersMaxZoom = maxZoom;
+ this._layersMinZoom = minZoom;
+ }
+ },
+
+ // map events
+
+ _initEvents: function () {
+ if (!L.DomEvent) { return; }
+
+ L.DomEvent.on(this._container, 'click', this._onMouseClick, this);
+
+ var events = ['dblclick', 'mousedown', 'mouseup', 'mouseenter',
+ 'mouseleave', 'mousemove', 'contextmenu'],
+ i, len;
+
+ for (i = 0, len = events.length; i < len; i++) {
+ L.DomEvent.on(this._container, events[i], this._fireMouseEvent, this);
+ }
+
+ if (this.options.trackResize) {
+ L.DomEvent.on(window, 'resize', this._onResize, this);
+ }
+ },
+
+ _onResize: function () {
+ L.Util.cancelAnimFrame(this._resizeRequest);
+ this._resizeRequest = L.Util.requestAnimFrame(
+ this.invalidateSize, this, false, this._container);
+ },
+
+ _onMouseClick: function (e) {
+ if (!this._loaded || (this.dragging && this.dragging.moved())) { return; }
+
+ this.fire('preclick');
+ this._fireMouseEvent(e);
+ },
+
+ _fireMouseEvent: function (e) {
+ if (!this._loaded) { return; }
+
+ var type = e.type;
+
+ type = (type === 'mouseenter' ? 'mouseover' : (type === 'mouseleave' ? 'mouseout' : type));
+
+ if (!this.hasEventListeners(type)) { return; }
+
+ if (type === 'contextmenu') {
+ L.DomEvent.preventDefault(e);
+ }
+
+ var containerPoint = this.mouseEventToContainerPoint(e),
+ layerPoint = this.containerPointToLayerPoint(containerPoint),
+ latlng = this.layerPointToLatLng(layerPoint);
+
+ this.fire(type, {
+ latlng: latlng,
+ layerPoint: layerPoint,
+ containerPoint: containerPoint,
+ originalEvent: e
+ });
+ },
+
+ _onTileLayerLoad: function () {
+ // TODO super-ugly, refactor!!!
+ // clear scaled tiles after all new tiles are loaded (for performance)
+ this._tileLayersToLoad--;
+ if (this._tileLayersNum && !this._tileLayersToLoad && this._tileBg) {
+ clearTimeout(this._clearTileBgTimer);
+ this._clearTileBgTimer = setTimeout(L.bind(this._clearTileBg, this), 500);
+ }
+ },
+
+ whenReady: function (callback, context) {
+ if (this._loaded) {
+ callback.call(context || this, this);
+ } else {
+ this.on('load', callback, context);
+ }
+ return this;
+ },
+
+
+ // private methods for getting map state
+
+ _getMapPanePos: function () {
+ return L.DomUtil.getPosition(this._mapPane);
+ },
+
+ _getTopLeftPoint: function () {
+ if (!this._loaded) {
+ throw new Error('Set map center and zoom first.');
+ }
+
+ return this._initialTopLeftPoint.subtract(this._getMapPanePos());
+ },
+
+ _getNewTopLeftPoint: function (center, zoom) {
+ var viewHalf = this.getSize()._divideBy(2);
+ // TODO round on display, not calculation to increase precision?
+ return this.project(center, zoom)._subtract(viewHalf)._round();
+ },
+
+ _latLngToNewLayerPoint: function (latlng, newZoom, newCenter) {
+ var topLeft = this._getNewTopLeftPoint(newCenter, newZoom).add(this._getMapPanePos());
+ return this.project(latlng, newZoom)._subtract(topLeft);
+ },
+
+ _getCenterLayerPoint: function () {
+ return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
+ },
+
+ _getCenterOffset: function (center) {
+ return this.latLngToLayerPoint(center).subtract(this._getCenterLayerPoint());
+ },
+
+ _limitZoom: function (zoom) {
+ var min = this.getMinZoom(),
+ max = this.getMaxZoom();
+
+ return Math.max(min, Math.min(max, zoom));
+ }
+});
+
+L.map = function (id, options) {
+ return new L.Map(id, options);
+};
+
+
+/*
+ * Mercator projection that takes into account that the Earth is not a perfect sphere.
+ * Less popular than spherical mercator; used by projections like EPSG:3395.
+ */
+
+L.Projection.Mercator = {
+ MAX_LATITUDE: 85.0840591556,
+
+ R_MINOR: 6356752.3142,
+ R_MAJOR: 6378137,
+
+ project: function (latlng) { // (LatLng) -> Point
+ var d = L.LatLng.DEG_TO_RAD,
+ max = this.MAX_LATITUDE,
+ lat = Math.max(Math.min(max, latlng.lat), -max),
+ r = this.R_MAJOR,
+ r2 = this.R_MINOR,
+ x = latlng.lng * d * r,
+ y = lat * d,
+ tmp = r2 / r,
+ eccent = Math.sqrt(1.0 - tmp * tmp),
+ con = eccent * Math.sin(y);
+
+ con = Math.pow((1 - con) / (1 + con), eccent * 0.5);
+
+ var ts = Math.tan(0.5 * ((Math.PI * 0.5) - y)) / con;
+ y = -r2 * Math.log(ts);
+
+ return new L.Point(x, y);
+ },
+
+ unproject: function (point) { // (Point, Boolean) -> LatLng
+ var d = L.LatLng.RAD_TO_DEG,
+ r = this.R_MAJOR,
+ r2 = this.R_MINOR,
+ lng = point.x * d / r,
+ tmp = r2 / r,
+ eccent = Math.sqrt(1 - (tmp * tmp)),
+ ts = Math.exp(- point.y / r2),
+ phi = (Math.PI / 2) - 2 * Math.atan(ts),
+ numIter = 15,
+ tol = 1e-7,
+ i = numIter,
+ dphi = 0.1,
+ con;
+
+ while ((Math.abs(dphi) > tol) && (--i > 0)) {
+ con = eccent * Math.sin(phi);
+ dphi = (Math.PI / 2) - 2 * Math.atan(ts *
+ Math.pow((1.0 - con) / (1.0 + con), 0.5 * eccent)) - phi;
+ phi += dphi;
+ }
+
+ return new L.LatLng(phi * d, lng);
+ }
+};
+
+
+
+L.CRS.EPSG3395 = L.extend({}, L.CRS, {
+ code: 'EPSG:3395',
+
+ projection: L.Projection.Mercator,
+
+ transformation: (function () {
+ var m = L.Projection.Mercator,
+ r = m.R_MAJOR,
+ r2 = m.R_MINOR;
+
+ return new L.Transformation(0.5 / (Math.PI * r), 0.5, -0.5 / (Math.PI * r2), 0.5);
+ }())
+});
+
+
+/*
+ * L.TileLayer is used for standard xyz-numbered tile layers.
+ */
+
+L.TileLayer = L.Class.extend({
+ includes: L.Mixin.Events,
+
+ options: {
+ minZoom: 0,
+ maxZoom: 18,
+ tileSize: 256,
+ subdomains: 'abc',
+ errorTileUrl: '',
+ attribution: '',
+ zoomOffset: 0,
+ opacity: 1,
+ /* (undefined works too)
+ zIndex: null,
+ tms: false,
+ continuousWorld: false,
+ noWrap: false,
+ zoomReverse: false,
+ detectRetina: false,
+ reuseTiles: false,
+ */
+ unloadInvisibleTiles: L.Browser.mobile,
+ updateWhenIdle: L.Browser.mobile
+ },
+
+ initialize: function (url, options) {
+ options = L.setOptions(this, options);
+
+ // detecting retina displays, adjusting tileSize and zoom levels
+ if (options.detectRetina && L.Browser.retina && options.maxZoom > 0) {
+
+ options.tileSize = Math.floor(options.tileSize / 2);
+ options.zoomOffset++;
+
+ if (options.minZoom > 0) {
+ options.minZoom--;
+ }
+ this.options.maxZoom--;
+ }
+
+ this._url = url;
+
+ var subdomains = this.options.subdomains;
+
+ if (typeof subdomains === 'string') {
+ this.options.subdomains = subdomains.split('');
+ }
+ },
+
+ onAdd: function (map) {
+ this._map = map;
+
+ // create a container div for tiles
+ this._initContainer();
+
+ // create an image to clone for tiles
+ this._createTileProto();
+
+ // set up events
+ map.on({
+ 'viewreset': this._resetCallback,
+ 'moveend': this._update
+ }, this);
+
+ if (!this.options.updateWhenIdle) {
+ this._limitedUpdate = L.Util.limitExecByInterval(this._update, 150, this);
+ map.on('move', this._limitedUpdate, this);
+ }
+
+ this._reset();
+ this._update();
+ },
+
+ addTo: function (map) {
+ map.addLayer(this);
+ return this;
+ },
+
+ onRemove: function (map) {
+ this._container.parentNode.removeChild(this._container);
+
+ map.off({
+ 'viewreset': this._resetCallback,
+ 'moveend': this._update
+ }, this);
+
+ if (!this.options.updateWhenIdle) {
+ map.off('move', this._limitedUpdate, this);
+ }
+
+ this._container = null;
+ this._map = null;
+ },
+
+ bringToFront: function () {
+ var pane = this._map._panes.tilePane;
+
+ if (this._container) {
+ pane.appendChild(this._container);
+ this._setAutoZIndex(pane, Math.max);
+ }
+
+ return this;
+ },
+
+ bringToBack: function () {
+ var pane = this._map._panes.tilePane;
+
+ if (this._container) {
+ pane.insertBefore(this._container, pane.firstChild);
+ this._setAutoZIndex(pane, Math.min);
+ }
+
+ return this;
+ },
+
+ getAttribution: function () {
+ return this.options.attribution;
+ },
+
+ setOpacity: function (opacity) {
+ this.options.opacity = opacity;
+
+ if (this._map) {
+ this._updateOpacity();
+ }
+
+ return this;
+ },
+
+ setZIndex: function (zIndex) {
+ this.options.zIndex = zIndex;
+ this._updateZIndex();
+
+ return this;
+ },
+
+ setUrl: function (url, noRedraw) {
+ this._url = url;
+
+ if (!noRedraw) {
+ this.redraw();
+ }
+
+ return this;
+ },
+
+ redraw: function () {
+ if (this._map) {
+ this._map._panes.tilePane.empty = false;
+ this._reset(true);
+ this._update();
+ }
+ return this;
+ },
+
+ _updateZIndex: function () {
+ if (this._container && this.options.zIndex !== undefined) {
+ this._container.style.zIndex = this.options.zIndex;
+ }
+ },
+
+ _setAutoZIndex: function (pane, compare) {
+
+ var layers = pane.children,
+ edgeZIndex = -compare(Infinity, -Infinity), // -Infinity for max, Infinity for min
+ zIndex, i, len;
+
+ for (i = 0, len = layers.length; i < len; i++) {
+
+ if (layers[i] !== this._container) {
+ zIndex = parseInt(layers[i].style.zIndex, 10);
+
+ if (!isNaN(zIndex)) {
+ edgeZIndex = compare(edgeZIndex, zIndex);
+ }
+ }
+ }
+
+ this.options.zIndex = this._container.style.zIndex =
+ (isFinite(edgeZIndex) ? edgeZIndex : 0) + compare(1, -1);
+ },
+
+ _updateOpacity: function () {
+ L.DomUtil.setOpacity(this._container, this.options.opacity);
+
+ // stupid webkit hack to force redrawing of tiles
+ var i,
+ tiles = this._tiles;
+
+ if (L.Browser.webkit) {
+ for (i in tiles) {
+ if (tiles.hasOwnProperty(i)) {
+ tiles[i].style.webkitTransform += ' translate(0,0)';
+ }
+ }
+ }
+ },
+
+ _initContainer: function () {
+ var tilePane = this._map._panes.tilePane;
+
+ if (!this._container || tilePane.empty) {
+ this._container = L.DomUtil.create('div', 'leaflet-layer');
+
+ this._updateZIndex();
+
+ tilePane.appendChild(this._container);
+
+ if (this.options.opacity < 1) {
+ this._updateOpacity();
+ }
+ }
+ },
+
+ _resetCallback: function (e) {
+ this._reset(e.hard);
+ },
+
+ _reset: function (clearOldContainer) {
+ var tiles = this._tiles;
+
+ for (var key in tiles) {
+ if (tiles.hasOwnProperty(key)) {
+ this.fire('tileunload', {tile: tiles[key]});
+ }
+ }
+
+ this._tiles = {};
+ this._tilesToLoad = 0;
+
+ if (this.options.reuseTiles) {
+ this._unusedTiles = [];
+ }
+
+ if (clearOldContainer && this._container) {
+ this._container.innerHTML = "";
+ }
+
+ this._initContainer();
+ },
+
+ _update: function () {
+
+ if (!this._map) { return; }
+
+ var bounds = this._map.getPixelBounds(),
+ zoom = this._map.getZoom(),
+ tileSize = this.options.tileSize;
+
+ if (zoom > this.options.maxZoom || zoom < this.options.minZoom) {
+ return;
+ }
+
+ var nwTilePoint = new L.Point(
+ Math.floor(bounds.min.x / tileSize),
+ Math.floor(bounds.min.y / tileSize)),
+
+ seTilePoint = new L.Point(
+ Math.floor(bounds.max.x / tileSize),
+ Math.floor(bounds.max.y / tileSize)),
+
+ tileBounds = new L.Bounds(nwTilePoint, seTilePoint);
+
+ this._addTilesFromCenterOut(tileBounds);
+
+ if (this.options.unloadInvisibleTiles || this.options.reuseTiles) {
+ this._removeOtherTiles(tileBounds);
+ }
+ },
+
+ _addTilesFromCenterOut: function (bounds) {
+ var queue = [],
+ center = bounds.getCenter();
+
+ var j, i, point;
+
+ for (j = bounds.min.y; j <= bounds.max.y; j++) {
+ for (i = bounds.min.x; i <= bounds.max.x; i++) {
+ point = new L.Point(i, j);
+
+ if (this._tileShouldBeLoaded(point)) {
+ queue.push(point);
+ }
+ }
+ }
+
+ var tilesToLoad = queue.length;
+
+ if (tilesToLoad === 0) { return; }
+
+ // load tiles in order of their distance to center
+ queue.sort(function (a, b) {
+ return a.distanceTo(center) - b.distanceTo(center);
+ });
+
+ var fragment = document.createDocumentFragment();
+
+ // if its the first batch of tiles to load
+ if (!this._tilesToLoad) {
+ this.fire('loading');
+ }
+
+ this._tilesToLoad += tilesToLoad;
+
+ for (i = 0; i < tilesToLoad; i++) {
+ this._addTile(queue[i], fragment);
+ }
+
+ this._container.appendChild(fragment);
+ },
+
+ _tileShouldBeLoaded: function (tilePoint) {
+ if ((tilePoint.x + ':' + tilePoint.y) in this._tiles) {
+ return false; // already loaded
+ }
+
+ if (!this.options.continuousWorld) {
+ var limit = this._getWrapTileNum();
+
+ if (this.options.noWrap && (tilePoint.x < 0 || tilePoint.x >= limit) ||
+ tilePoint.y < 0 || tilePoint.y >= limit) {
+ return false; // exceeds world bounds
+ }
+ }
+
+ return true;
+ },
+
+ _removeOtherTiles: function (bounds) {
+ var kArr, x, y, key;
+
+ for (key in this._tiles) {
+ if (this._tiles.hasOwnProperty(key)) {
+ kArr = key.split(':');
+ x = parseInt(kArr[0], 10);
+ y = parseInt(kArr[1], 10);
+
+ // remove tile if it's out of bounds
+ if (x < bounds.min.x || x > bounds.max.x || y < bounds.min.y || y > bounds.max.y) {
+ this._removeTile(key);
+ }
+ }
+ }
+ },
+
+ _removeTile: function (key) {
+ var tile = this._tiles[key];
+
+ this.fire("tileunload", {tile: tile, url: tile.src});
+
+ if (this.options.reuseTiles) {
+ L.DomUtil.removeClass(tile, 'leaflet-tile-loaded');
+ this._unusedTiles.push(tile);
+
+ } else if (tile.parentNode === this._container) {
+ this._container.removeChild(tile);
+ }
+
+ // for https://github.com/CloudMade/Leaflet/issues/137
+ if (!L.Browser.android) {
+ tile.src = L.Util.emptyImageUrl;
+ }
+
+ delete this._tiles[key];
+ },
+
+ _addTile: function (tilePoint, container) {
+ var tilePos = this._getTilePos(tilePoint);
+
+ // get unused tile - or create a new tile
+ var tile = this._getTile();
+
+ /*
+ Chrome 20 layouts much faster with top/left (verify with timeline, frames)
+ Android 4 browser has display issues with top/left and requires transform instead
+ Android 3 browser not tested
+ Android 2 browser requires top/left or tiles disappear on load or first drag
+ (reappear after zoom) https://github.com/CloudMade/Leaflet/issues/866
+ (other browsers don't currently care) - see debug/hacks/jitter.html for an example
+ */
+ L.DomUtil.setPosition(tile, tilePos, L.Browser.chrome || L.Browser.android23);
+
+ this._tiles[tilePoint.x + ':' + tilePoint.y] = tile;
+
+ this._loadTile(tile, tilePoint);
+
+ if (tile.parentNode !== this._container) {
+ container.appendChild(tile);
+ }
+ },
+
+ _getZoomForUrl: function () {
+
+ var options = this.options,
+ zoom = this._map.getZoom();
+
+ if (options.zoomReverse) {
+ zoom = options.maxZoom - zoom;
+ }
+
+ return zoom + options.zoomOffset;
+ },
+
+ _getTilePos: function (tilePoint) {
+ var origin = this._map.getPixelOrigin(),
+ tileSize = this.options.tileSize;
+
+ return tilePoint.multiplyBy(tileSize).subtract(origin);
+ },
+
+ // image-specific code (override to implement e.g. Canvas or SVG tile layer)
+
+ getTileUrl: function (tilePoint) {
+ this._adjustTilePoint(tilePoint);
+
+ return L.Util.template(this._url, L.extend({
+ s: this._getSubdomain(tilePoint),
+ z: this._getZoomForUrl(),
+ x: tilePoint.x,
+ y: tilePoint.y
+ }, this.options));
+ },
+
+ _getWrapTileNum: function () {
+ // TODO refactor, limit is not valid for non-standard projections
+ return Math.pow(2, this._getZoomForUrl());
+ },
+
+ _adjustTilePoint: function (tilePoint) {
+
+ var limit = this._getWrapTileNum();
+
+ // wrap tile coordinates
+ if (!this.options.continuousWorld && !this.options.noWrap) {
+ tilePoint.x = ((tilePoint.x % limit) + limit) % limit;
+ }
+
+ if (this.options.tms) {
+ tilePoint.y = limit - tilePoint.y - 1;
+ }
+ },
+
+ _getSubdomain: function (tilePoint) {
+ var index = (tilePoint.x + tilePoint.y) % this.options.subdomains.length;
+ return this.options.subdomains[index];
+ },
+
+ _createTileProto: function () {
+ var img = this._tileImg = L.DomUtil.create('img', 'leaflet-tile');
+ img.style.width = img.style.height = this.options.tileSize + 'px';
+ img.galleryimg = 'no';
+ },
+
+ _getTile: function () {
+ if (this.options.reuseTiles && this._unusedTiles.length > 0) {
+ var tile = this._unusedTiles.pop();
+ this._resetTile(tile);
+ return tile;
+ }
+ return this._createTile();
+ },
+
+ // Override if data stored on a tile needs to be cleaned up before reuse
+ _resetTile: function (/*tile*/) {},
+
+ _createTile: function () {
+ var tile = this._tileImg.cloneNode(false);
+ tile.onselectstart = tile.onmousemove = L.Util.falseFn;
+ return tile;
+ },
+
+ _loadTile: function (tile, tilePoint) {
+ tile._layer = this;
+ tile.onload = this._tileOnLoad;
+ tile.onerror = this._tileOnError;
+
+ tile.src = this.getTileUrl(tilePoint);
+ },
+
+ _tileLoaded: function () {
+ this._tilesToLoad--;
+ if (!this._tilesToLoad) {
+ this.fire('load');
+ }
+ },
+
+ _tileOnLoad: function () {
+ var layer = this._layer;
+
+ //Only if we are loading an actual image
+ if (this.src !== L.Util.emptyImageUrl) {
+ L.DomUtil.addClass(this, 'leaflet-tile-loaded');
+
+ layer.fire('tileload', {
+ tile: this,
+ url: this.src
+ });
+ }
+
+ layer._tileLoaded();
+ },
+
+ _tileOnError: function () {
+ var layer = this._layer;
+
+ layer.fire('tileerror', {
+ tile: this,
+ url: this.src
+ });
+
+ var newUrl = layer.options.errorTileUrl;
+ if (newUrl) {
+ this.src = newUrl;
+ }
+
+ layer._tileLoaded();
+ }
+});
+
+L.tileLayer = function (url, options) {
+ return new L.TileLayer(url, options);
+};
+
+
+/*
+ * L.TileLayer.WMS is used for putting WMS tile layers on the map.
+ */
+
+L.TileLayer.WMS = L.TileLayer.extend({
+
+ defaultWmsParams: {
+ service: 'WMS',
+ request: 'GetMap',
+ version: '1.1.1',
+ layers: '',
+ styles: '',
+ format: 'image/jpeg',
+ transparent: false
+ },
+
+ initialize: function (url, options) { // (String, Object)
+
+ this._url = url;
+
+ var wmsParams = L.extend({}, this.defaultWmsParams);
+
+ if (options.detectRetina && L.Browser.retina) {
+ wmsParams.width = wmsParams.height = this.options.tileSize * 2;
+ } else {
+ wmsParams.width = wmsParams.height = this.options.tileSize;
+ }
+
+ for (var i in options) {
+ // all keys that are not TileLayer options go to WMS params
+ if (!this.options.hasOwnProperty(i)) {
+ wmsParams[i] = options[i];
+ }
+ }
+
+ this.wmsParams = wmsParams;
+
+ L.setOptions(this, options);
+ },
+
+ onAdd: function (map) {
+
+ var projectionKey = parseFloat(this.wmsParams.version) >= 1.3 ? 'crs' : 'srs';
+ this.wmsParams[projectionKey] = map.options.crs.code;
+
+ L.TileLayer.prototype.onAdd.call(this, map);
+ },
+
+ getTileUrl: function (tilePoint, zoom) { // (Point, Number) -> String
+
+ this._adjustTilePoint(tilePoint);
+
+ var map = this._map,
+ crs = map.options.crs,
+ tileSize = this.options.tileSize,
+
+ nwPoint = tilePoint.multiplyBy(tileSize),
+ sePoint = nwPoint.add(new L.Point(tileSize, tileSize)),
+
+ nw = crs.project(map.unproject(nwPoint, zoom)),
+ se = crs.project(map.unproject(sePoint, zoom)),
+
+ bbox = [nw.x, se.y, se.x, nw.y].join(','),
+
+ url = L.Util.template(this._url, {s: this._getSubdomain(tilePoint)});
+
+ return url + L.Util.getParamString(this.wmsParams, url) + "&bbox=" + bbox;
+ },
+
+ setParams: function (params, noRedraw) {
+
+ L.extend(this.wmsParams, params);
+
+ if (!noRedraw) {
+ this.redraw();
+ }
+
+ return this;
+ }
+});
+
+L.tileLayer.wms = function (url, options) {
+ return new L.TileLayer.WMS(url, options);
+};
+
+
+/*
+ * L.TileLayer.Canvas is a class that you can use as a base for creating
+ * dynamically drawn Canvas-based tile layers.
+ */
+
+L.TileLayer.Canvas = L.TileLayer.extend({
+ options: {
+ async: false
+ },
+
+ initialize: function (options) {
+ L.setOptions(this, options);
+ },
+
+ redraw: function () {
+ var tiles = this._tiles;
+
+ for (var i in tiles) {
+ if (tiles.hasOwnProperty(i)) {
+ this._redrawTile(tiles[i]);
+ }
+ }
+ },
+
+ _redrawTile: function (tile) {
+ this.drawTile(tile, tile._tilePoint, this._map._zoom);
+ },
+
+ _createTileProto: function () {
+ var proto = this._canvasProto = L.DomUtil.create('canvas', 'leaflet-tile');
+ proto.width = proto.height = this.options.tileSize;
+ },
+
+ _createTile: function () {
+ var tile = this._canvasProto.cloneNode(false);
+ tile.onselectstart = tile.onmousemove = L.Util.falseFn;
+ return tile;
+ },
+
+ _loadTile: function (tile, tilePoint) {
+ tile._layer = this;
+ tile._tilePoint = tilePoint;
+
+ this._redrawTile(tile);
+
+ if (!this.options.async) {
+ this.tileDrawn(tile);
+ }
+ },
+
+ drawTile: function (/*tile, tilePoint*/) {
+ // override with rendering code
+ },
+
+ tileDrawn: function (tile) {
+ this._tileOnLoad.call(tile);
+ }
+});
+
+
+L.tileLayer.canvas = function (options) {
+ return new L.TileLayer.Canvas(options);
+};
+
+
+/*
+ * L.ImageOverlay is used to overlay images over the map (to specific geographical bounds).
+ */
+
+L.ImageOverlay = L.Class.extend({
+ includes: L.Mixin.Events,
+
+ options: {
+ opacity: 1
+ },
+
+ initialize: function (url, bounds, options) { // (String, LatLngBounds, Object)
+ this._url = url;
+ this._bounds = L.latLngBounds(bounds);
+
+ L.setOptions(this, options);
+ },
+
+ onAdd: function (map) {
+ this._map = map;
+
+ if (!this._image) {
+ this._initImage();
+ }
+
+ map._panes.overlayPane.appendChild(this._image);
+
+ map.on('viewreset', this._reset, this);
+
+ if (map.options.zoomAnimation && L.Browser.any3d) {
+ map.on('zoomanim', this._animateZoom, this);
+ }
+
+ this._reset();
+ },
+
+ onRemove: function (map) {
+ map.getPanes().overlayPane.removeChild(this._image);
+
+ map.off('viewreset', this._reset, this);
+
+ if (map.options.zoomAnimation) {
+ map.off('zoomanim', this._animateZoom, this);
+ }
+ },
+
+ addTo: function (map) {
+ map.addLayer(this);
+ return this;
+ },
+
+ setOpacity: function (opacity) {
+ this.options.opacity = opacity;
+ this._updateOpacity();
+ return this;
+ },
+
+ // TODO remove bringToFront/bringToBack duplication from TileLayer/Path
+ bringToFront: function () {
+ if (this._image) {
+ this._map._panes.overlayPane.appendChild(this._image);
+ }
+ return this;
+ },
+
+ bringToBack: function () {
+ var pane = this._map._panes.overlayPane;
+ if (this._image) {
+ pane.insertBefore(this._image, pane.firstChild);
+ }
+ return this;
+ },
+
+ _initImage: function () {
+ this._image = L.DomUtil.create('img', 'leaflet-image-layer');
+
+ if (this._map.options.zoomAnimation && L.Browser.any3d) {
+ L.DomUtil.addClass(this._image, 'leaflet-zoom-animated');
+ } else {
+ L.DomUtil.addClass(this._image, 'leaflet-zoom-hide');
+ }
+
+ this._updateOpacity();
+
+ //TODO createImage util method to remove duplication
+ L.extend(this._image, {
+ galleryimg: 'no',
+ onselectstart: L.Util.falseFn,
+ onmousemove: L.Util.falseFn,
+ onload: L.bind(this._onImageLoad, this),
+ src: this._url
+ });
+ },
+
+ _animateZoom: function (e) {
+ var map = this._map,
+ image = this._image,
+ scale = map.getZoomScale(e.zoom),
+ nw = this._bounds.getNorthWest(),
+ se = this._bounds.getSouthEast(),
+
+ topLeft = map._latLngToNewLayerPoint(nw, e.zoom, e.center),
+ size = map._latLngToNewLayerPoint(se, e.zoom, e.center)._subtract(topLeft),
+ origin = topLeft._add(size._multiplyBy((1 / 2) * (1 - 1 / scale)));
+
+ image.style[L.DomUtil.TRANSFORM] =
+ L.DomUtil.getTranslateString(origin) + ' scale(' + scale + ') ';
+ },
+
+ _reset: function () {
+ var image = this._image,
+ topLeft = this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
+ size = this._map.latLngToLayerPoint(this._bounds.getSouthEast())._subtract(topLeft);
+
+ L.DomUtil.setPosition(image, topLeft);
+
+ image.style.width = size.x + 'px';
+ image.style.height = size.y + 'px';
+ },
+
+ _onImageLoad: function () {
+ this.fire('load');
+ },
+
+ _updateOpacity: function () {
+ L.DomUtil.setOpacity(this._image, this.options.opacity);
+ }
+});
+
+L.imageOverlay = function (url, bounds, options) {
+ return new L.ImageOverlay(url, bounds, options);
+};
+
+
+/*
+ * L.Icon is an image-based icon class that you can use with L.Marker for custom markers.
+ */
+
+L.Icon = L.Class.extend({
+ options: {
+ /*
+ iconUrl: (String) (required)
+ iconRetinaUrl: (String) (optional, used for retina devices if detected)
+ iconSize: (Point) (can be set through CSS)
+ iconAnchor: (Point) (centered by default, can be set in CSS with negative margins)
+ popupAnchor: (Point) (if not specified, popup opens in the anchor point)
+ shadowUrl: (Point) (no shadow by default)
+ shadowRetinaUrl: (String) (optional, used for retina devices if detected)
+ shadowSize: (Point)
+ shadowAnchor: (Point)
+ */
+ className: ''
+ },
+
+ initialize: function (options) {
+ L.setOptions(this, options);
+ },
+
+ createIcon: function () {
+ return this._createIcon('icon');
+ },
+
+ createShadow: function () {
+ return this._createIcon('shadow');
+ },
+
+ _createIcon: function (name) {
+ var src = this._getIconUrl(name);
+
+ if (!src) {
+ if (name === 'icon') {
+ throw new Error("iconUrl not set in Icon options (see the docs).");
+ }
+ return null;
+ }
+
+ var img = this._createImg(src);
+ this._setIconStyles(img, name);
+
+ return img;
+ },
+
+ _setIconStyles: function (img, name) {
+ var options = this.options,
+ size = L.point(options[name + 'Size']),
+ anchor;
+
+ if (name === 'shadow') {
+ anchor = L.point(options.shadowAnchor || options.iconAnchor);
+ } else {
+ anchor = L.point(options.iconAnchor);
+ }
+
+ if (!anchor && size) {
+ anchor = size.divideBy(2, true);
+ }
+
+ img.className = 'leaflet-marker-' + name + ' ' + options.className;
+
+ if (anchor) {
+ img.style.marginLeft = (-anchor.x) + 'px';
+ img.style.marginTop = (-anchor.y) + 'px';
+ }
+
+ if (size) {
+ img.style.width = size.x + 'px';
+ img.style.height = size.y + 'px';
+ }
+ },
+
+ _createImg: function (src) {
+ var el;
+
+ if (!L.Browser.ie6) {
+ el = document.createElement('img');
+ el.src = src;
+ } else {
+ el = document.createElement('div');
+ el.style.filter =
+ 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="' + src + '")';
+ }
+ return el;
+ },
+
+ _getIconUrl: function (name) {
+ if (L.Browser.retina && this.options[name + 'RetinaUrl']) {
+ return this.options[name + 'RetinaUrl'];
+ }
+ return this.options[name + 'Url'];
+ }
+});
+
+L.icon = function (options) {
+ return new L.Icon(options);
+};
+
+
+/*
+ * L.Icon.Default is the blue marker icon used by default in Leaflet.
+ */
+
+L.Icon.Default = L.Icon.extend({
+
+ options: {
+ iconSize: new L.Point(25, 41),
+ iconAnchor: new L.Point(12, 41),
+ popupAnchor: new L.Point(1, -34),
+
+ shadowSize: new L.Point(41, 41)
+ },
+
+ _getIconUrl: function (name) {
+ var key = name + 'Url';
+
+ if (this.options[key]) {
+ return this.options[key];
+ }
+
+ if (L.Browser.retina && name === 'icon') {
+ name += '@2x';
+ }
+
+ var path = L.Icon.Default.imagePath;
+
+ if (!path) {
+ throw new Error("Couldn't autodetect L.Icon.Default.imagePath, set it manually.");
+ }
+
+ return path + '/marker-' + name + '.png';
+ }
+});
+
+L.Icon.Default.imagePath = (function () {
+ var scripts = document.getElementsByTagName('script'),
+ leafletRe = /\/?leaflet[\-\._]?([\w\-\._]*)\.js\??/;
+
+ var i, len, src, matches;
+
+ for (i = 0, len = scripts.length; i < len; i++) {
+ src = scripts[i].src;
+ matches = src.match(leafletRe);
+
+ if (matches) {
+ return src.split(leafletRe)[0] + '/images';
+ }
+ }
+}());
+
+
+/*
+ * L.Marker is used to display clickable/draggable icons on the map.
+ */
+
+L.Marker = L.Class.extend({
+
+ includes: L.Mixin.Events,
+
+ options: {
+ icon: new L.Icon.Default(),
+ title: '',
+ clickable: true,
+ draggable: false,
+ zIndexOffset: 0,
+ opacity: 1,
+ riseOnHover: false,
+ riseOffset: 250
+ },
+
+ initialize: function (latlng, options) {
+ L.setOptions(this, options);
+ this._latlng = L.latLng(latlng);
+ },
+
+ onAdd: function (map) {
+ this._map = map;
+
+ map.on('viewreset', this.update, this);
+
+ this._initIcon();
+ this.update();
+
+ if (map.options.zoomAnimation && map.options.markerZoomAnimation) {
+ map.on('zoomanim', this._animateZoom, this);
+ }
+ },
+
+ addTo: function (map) {
+ map.addLayer(this);
+ return this;
+ },
+
+ onRemove: function (map) {
+ this._removeIcon();
+
+ this.fire('remove');
+
+ map.off({
+ 'viewreset': this.update,
+ 'zoomanim': this._animateZoom
+ }, this);
+
+ this._map = null;
+ },
+
+ getLatLng: function () {
+ return this._latlng;
+ },
+
+ setLatLng: function (latlng) {
+ this._latlng = L.latLng(latlng);
+
+ this.update();
+
+ return this.fire('move', { latlng: this._latlng });
+ },
+
+ setZIndexOffset: function (offset) {
+ this.options.zIndexOffset = offset;
+ this.update();
+
+ return this;
+ },
+
+ setIcon: function (icon) {
+ if (this._map) {
+ this._removeIcon();
+ }
+
+ this.options.icon = icon;
+
+ if (this._map) {
+ this._initIcon();
+ this.update();
+ }
+
+ return this;
+ },
+
+ update: function () {
+ if (this._icon) {
+ var pos = this._map.latLngToLayerPoint(this._latlng).round();
+ this._setPos(pos);
+ }
+
+ return this;
+ },
+
+ _initIcon: function () {
+ var options = this.options,
+ map = this._map,
+ animation = (map.options.zoomAnimation && map.options.markerZoomAnimation),
+ classToAdd = animation ? 'leaflet-zoom-animated' : 'leaflet-zoom-hide',
+ needOpacityUpdate = false;
+
+ if (!this._icon) {
+ this._icon = options.icon.createIcon();
+
+ if (options.title) {
+ this._icon.title = options.title;
+ }
+
+ this._initInteraction();
+ needOpacityUpdate = (this.options.opacity < 1);
+
+ L.DomUtil.addClass(this._icon, classToAdd);
+
+ if (options.riseOnHover) {
+ L.DomEvent
+ .on(this._icon, 'mouseover', this._bringToFront, this)
+ .on(this._icon, 'mouseout', this._resetZIndex, this);
+ }
+ }
+
+ if (!this._shadow) {
+ this._shadow = options.icon.createShadow();
+
+ if (this._shadow) {
+ L.DomUtil.addClass(this._shadow, classToAdd);
+ needOpacityUpdate = (this.options.opacity < 1);
+ }
+ }
+
+ if (needOpacityUpdate) {
+ this._updateOpacity();
+ }
+
+ var panes = this._map._panes;
+
+ panes.markerPane.appendChild(this._icon);
+
+ if (this._shadow) {
+ panes.shadowPane.appendChild(this._shadow);
+ }
+ },
+
+ _removeIcon: function () {
+ var panes = this._map._panes;
+
+ if (this.options.riseOnHover) {
+ L.DomEvent
+ .off(this._icon, 'mouseover', this._bringToFront)
+ .off(this._icon, 'mouseout', this._resetZIndex);
+ }
+
+ panes.markerPane.removeChild(this._icon);
+
+ if (this._shadow) {
+ panes.shadowPane.removeChild(this._shadow);
+ }
+
+ this._icon = this._shadow = null;
+ },
+
+ _setPos: function (pos) {
+ L.DomUtil.setPosition(this._icon, pos);
+
+ if (this._shadow) {
+ L.DomUtil.setPosition(this._shadow, pos);
+ }
+
+ this._zIndex = pos.y + this.options.zIndexOffset;
+
+ this._resetZIndex();
+ },
+
+ _updateZIndex: function (offset) {
+ this._icon.style.zIndex = this._zIndex + offset;
+ },
+
+ _animateZoom: function (opt) {
+ var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center);
+
+ this._setPos(pos);
+ },
+
+ _initInteraction: function () {
+
+ if (!this.options.clickable) { return; }
+
+ // TODO refactor into something shared with Map/Path/etc. to DRY it up
+
+ var icon = this._icon,
+ events = ['dblclick', 'mousedown', 'mouseover', 'mouseout', 'contextmenu'];
+
+ L.DomUtil.addClass(icon, 'leaflet-clickable');
+ L.DomEvent.on(icon, 'click', this._onMouseClick, this);
+
+ for (var i = 0; i < events.length; i++) {
+ L.DomEvent.on(icon, events[i], this._fireMouseEvent, this);
+ }
+
+ if (L.Handler.MarkerDrag) {
+ this.dragging = new L.Handler.MarkerDrag(this);
+
+ if (this.options.draggable) {
+ this.dragging.enable();
+ }
+ }
+ },
+
+ _onMouseClick: function (e) {
+ var wasDragged = this.dragging && this.dragging.moved();
+
+ if (this.hasEventListeners(e.type) || wasDragged) {
+ L.DomEvent.stopPropagation(e);
+ }
+
+ if (wasDragged) { return; }
+
+ if ((!this.dragging || !this.dragging._enabled) && this._map.dragging && this._map.dragging.moved()) { return; }
+
+ this.fire(e.type, {
+ originalEvent: e
+ });
+ },
+
+ _fireMouseEvent: function (e) {
+
+ this.fire(e.type, {
+ originalEvent: e
+ });
+
+ // TODO proper custom event propagation
+ // this line will always be called if marker is in a FeatureGroup
+ if (e.type === 'contextmenu' && this.hasEventListeners(e.type)) {
+ L.DomEvent.preventDefault(e);
+ }
+ if (e.type !== 'mousedown') {
+ L.DomEvent.stopPropagation(e);
+ }
+ },
+
+ setOpacity: function (opacity) {
+ this.options.opacity = opacity;
+ if (this._map) {
+ this._updateOpacity();
+ }
+ },
+
+ _updateOpacity: function () {
+ L.DomUtil.setOpacity(this._icon, this.options.opacity);
+ if (this._shadow) {
+ L.DomUtil.setOpacity(this._shadow, this.options.opacity);
+ }
+ },
+
+ _bringToFront: function () {
+ this._updateZIndex(this.options.riseOffset);
+ },
+
+ _resetZIndex: function () {
+ this._updateZIndex(0);
+ }
+});
+
+L.marker = function (latlng, options) {
+ return new L.Marker(latlng, options);
+};
+
+
+/*
+ * L.DivIcon is a lightweight HTML-based icon class (as opposed to the image-based L.Icon)
+ * to use with L.Marker.
+ */
+
+L.DivIcon = L.Icon.extend({
+ options: {
+ iconSize: new L.Point(12, 12), // also can be set through CSS
+ /*
+ iconAnchor: (Point)
+ popupAnchor: (Point)
+ html: (String)
+ bgPos: (Point)
+ */
+ className: 'leaflet-div-icon'
+ },
+
+ createIcon: function () {
+ var div = document.createElement('div'),
+ options = this.options;
+
+ if (options.html) {
+ div.innerHTML = options.html;
+ }
+
+ if (options.bgPos) {
+ div.style.backgroundPosition =
+ (-options.bgPos.x) + 'px ' + (-options.bgPos.y) + 'px';
+ }
+
+ this._setIconStyles(div, 'icon');
+ return div;
+ },
+
+ createShadow: function () {
+ return null;
+ }
+});
+
+L.divIcon = function (options) {
+ return new L.DivIcon(options);
+};
+
+
+/*
+ * L.Popup is used for displaying popups on the map.
+ */
+
+L.Map.mergeOptions({
+ closePopupOnClick: true
+});
+
+L.Popup = L.Class.extend({
+ includes: L.Mixin.Events,
+
+ options: {
+ minWidth: 50,
+ maxWidth: 300,
+ maxHeight: null,
+ autoPan: true,
+ closeButton: true,
+ offset: new L.Point(0, 6),
+ autoPanPadding: new L.Point(5, 5),
+ className: '',
+ zoomAnimation: true
+ },
+
+ initialize: function (options, source) {
+ L.setOptions(this, options);
+
+ this._source = source;
+ this._animated = L.Browser.any3d && this.options.zoomAnimation;
+ },
+
+ onAdd: function (map) {
+ this._map = map;
+
+ if (!this._container) {
+ this._initLayout();
+ }
+ this._updateContent();
+
+ var animFade = map.options.fadeAnimation;
+
+ if (animFade) {
+ L.DomUtil.setOpacity(this._container, 0);
+ }
+ map._panes.popupPane.appendChild(this._container);
+
+ map.on('viewreset', this._updatePosition, this);
+
+ if (this._animated) {
+ map.on('zoomanim', this._zoomAnimation, this);
+ }
+
+ if (map.options.closePopupOnClick) {
+ map.on('preclick', this._close, this);
+ }
+
+ this._update();
+
+ if (animFade) {
+ L.DomUtil.setOpacity(this._container, 1);
+ }
+ },
+
+ addTo: function (map) {
+ map.addLayer(this);
+ return this;
+ },
+
+ openOn: function (map) {
+ map.openPopup(this);
+ return this;
+ },
+
+ onRemove: function (map) {
+ map._panes.popupPane.removeChild(this._container);
+
+ L.Util.falseFn(this._container.offsetWidth); // force reflow
+
+ map.off({
+ viewreset: this._updatePosition,
+ preclick: this._close,
+ zoomanim: this._zoomAnimation
+ }, this);
+
+ if (map.options.fadeAnimation) {
+ L.DomUtil.setOpacity(this._container, 0);
+ }
+
+ this._map = null;
+ },
+
+ setLatLng: function (latlng) {
+ this._latlng = L.latLng(latlng);
+ this._update();
+ return this;
+ },
+
+ setContent: function (content) {
+ this._content = content;
+ this._update();
+ return this;
+ },
+
+ _close: function () {
+ var map = this._map;
+
+ if (map) {
+ map._popup = null;
+
+ map
+ .removeLayer(this)
+ .fire('popupclose', {popup: this});
+ }
+ },
+
+ _initLayout: function () {
+ var prefix = 'leaflet-popup',
+ containerClass = prefix + ' ' + this.options.className + ' leaflet-zoom-' +
+ (this._animated ? 'animated' : 'hide'),
+ container = this._container = L.DomUtil.create('div', containerClass),
+ closeButton;
+
+ if (this.options.closeButton) {
+ closeButton = this._closeButton =
+ L.DomUtil.create('a', prefix + '-close-button', container);
+ closeButton.href = '#close';
+ closeButton.innerHTML = '×';
+
+ L.DomEvent.on(closeButton, 'click', this._onCloseButtonClick, this);
+ }
+
+ var wrapper = this._wrapper =
+ L.DomUtil.create('div', prefix + '-content-wrapper', container);
+ L.DomEvent.disableClickPropagation(wrapper);
+
+ this._contentNode = L.DomUtil.create('div', prefix + '-content', wrapper);
+ L.DomEvent.on(this._contentNode, 'mousewheel', L.DomEvent.stopPropagation);
+
+ this._tipContainer = L.DomUtil.create('div', prefix + '-tip-container', container);
+ this._tip = L.DomUtil.create('div', prefix + '-tip', this._tipContainer);
+ },
+
+ _update: function () {
+ if (!this._map) { return; }
+
+ this._container.style.visibility = 'hidden';
+
+ this._updateContent();
+ this._updateLayout();
+ this._updatePosition();
+
+ this._container.style.visibility = '';
+
+ this._adjustPan();
+ },
+
+ _updateContent: function () {
+ if (!this._content) { return; }
+
+ if (typeof this._content === 'string') {
+ this._contentNode.innerHTML = this._content;
+ } else {
+ while (this._contentNode.hasChildNodes()) {
+ this._contentNode.removeChild(this._contentNode.firstChild);
+ }
+ this._contentNode.appendChild(this._content);
+ }
+ this.fire('contentupdate');
+ },
+
+ _updateLayout: function () {
+ var container = this._contentNode,
+ style = container.style;
+
+ style.width = '';
+ style.whiteSpace = 'nowrap';
+
+ var width = container.offsetWidth;
+ width = Math.min(width, this.options.maxWidth);
+ width = Math.max(width, this.options.minWidth);
+
+ style.width = (width + 1) + 'px';
+ style.whiteSpace = '';
+
+ style.height = '';
+
+ var height = container.offsetHeight,
+ maxHeight = this.options.maxHeight,
+ scrolledClass = 'leaflet-popup-scrolled';
+
+ if (maxHeight && height > maxHeight) {
+ style.height = maxHeight + 'px';
+ L.DomUtil.addClass(container, scrolledClass);
+ } else {
+ L.DomUtil.removeClass(container, scrolledClass);
+ }
+
+ this._containerWidth = this._container.offsetWidth;
+ },
+
+ _updatePosition: function () {
+ if (!this._map) { return; }
+
+ var pos = this._map.latLngToLayerPoint(this._latlng),
+ animated = this._animated,
+ offset = this.options.offset;
+
+ if (animated) {
+ L.DomUtil.setPosition(this._container, pos);
+ }
+
+ this._containerBottom = -offset.y - (animated ? 0 : pos.y);
+ this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x + (animated ? 0 : pos.x);
+
+ //Bottom position the popup in case the height of the popup changes (images loading etc)
+ this._container.style.bottom = this._containerBottom + 'px';
+ this._container.style.left = this._containerLeft + 'px';
+ },
+
+ _zoomAnimation: function (opt) {
+ var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center);
+
+ L.DomUtil.setPosition(this._container, pos);
+ },
+
+ _adjustPan: function () {
+ if (!this.options.autoPan) { return; }
+
+ var map = this._map,
+ containerHeight = this._container.offsetHeight,
+ containerWidth = this._containerWidth,
+
+ layerPos = new L.Point(this._containerLeft, -containerHeight - this._containerBottom);
+
+ if (this._animated) {
+ layerPos._add(L.DomUtil.getPosition(this._container));
+ }
+
+ var containerPos = map.layerPointToContainerPoint(layerPos),
+ padding = this.options.autoPanPadding,
+ size = map.getSize(),
+ dx = 0,
+ dy = 0;
+
+ if (containerPos.x < 0) {
+ dx = containerPos.x - padding.x;
+ }
+ if (containerPos.x + containerWidth > size.x) {
+ dx = containerPos.x + containerWidth - size.x + padding.x;
+ }
+ if (containerPos.y < 0) {
+ dy = containerPos.y - padding.y;
+ }
+ if (containerPos.y + containerHeight > size.y) {
+ dy = containerPos.y + containerHeight - size.y + padding.y;
+ }
+
+ if (dx || dy) {
+ map.panBy(new L.Point(dx, dy));
+ }
+ },
+
+ _onCloseButtonClick: function (e) {
+ this._close();
+ L.DomEvent.stop(e);
+ }
+});
+
+L.popup = function (options, source) {
+ return new L.Popup(options, source);
+};
+
+
+/*
+ * Popup extension to L.Marker, adding popup-related methods.
+ */
+
+L.Marker.include({
+ openPopup: function () {
+ if (this._popup && this._map) {
+ this._popup.setLatLng(this._latlng);
+ this._map.openPopup(this._popup);
+ }
+
+ return this;
+ },
+
+ closePopup: function () {
+ if (this._popup) {
+ this._popup._close();
+ }
+ return this;
+ },
+
+ bindPopup: function (content, options) {
+ var anchor = L.point(this.options.icon.options.popupAnchor) || new L.Point(0, 0);
+
+ anchor = anchor.add(L.Popup.prototype.options.offset);
+
+ if (options && options.offset) {
+ anchor = anchor.add(options.offset);
+ }
+
+ options = L.extend({offset: anchor}, options);
+
+ if (!this._popup) {
+ this
+ .on('click', this.openPopup, this)
+ .on('remove', this.closePopup, this)
+ .on('move', this._movePopup, this);
+ }
+
+ this._popup = new L.Popup(options, this)
+ .setContent(content);
+
+ return this;
+ },
+
+ unbindPopup: function () {
+ if (this._popup) {
+ this._popup = null;
+ this
+ .off('click', this.openPopup)
+ .off('remove', this.closePopup)
+ .off('move', this._movePopup);
+ }
+ return this;
+ },
+
+ _movePopup: function (e) {
+ this._popup.setLatLng(e.latlng);
+ }
+});
+
+
+/*
+ * Adds popup-related methods to L.Map.
+ */
+
+L.Map.include({
+ openPopup: function (popup) {
+ this.closePopup();
+
+ this._popup = popup;
+
+ return this
+ .addLayer(popup)
+ .fire('popupopen', {popup: this._popup});
+ },
+
+ closePopup: function () {
+ if (this._popup) {
+ this._popup._close();
+ }
+ return this;
+ }
+});
+
+
+/*
+ * L.LayerGroup is a class to combine several layers into one so that
+ * you can manipulate the group (e.g. add/remove it) as one layer.
+ */
+
+L.LayerGroup = L.Class.extend({
+ initialize: function (layers) {
+ this._layers = {};
+
+ var i, len;
+
+ if (layers) {
+ for (i = 0, len = layers.length; i < len; i++) {
+ this.addLayer(layers[i]);
+ }
+ }
+ },
+
+ addLayer: function (layer) {
+ var id = L.stamp(layer);
+
+ this._layers[id] = layer;
+
+ if (this._map) {
+ this._map.addLayer(layer);
+ }
+
+ return this;
+ },
+
+ removeLayer: function (layer) {
+ var id = L.stamp(layer);
+
+ delete this._layers[id];
+
+ if (this._map) {
+ this._map.removeLayer(layer);
+ }
+
+ return this;
+ },
+
+ clearLayers: function () {
+ this.eachLayer(this.removeLayer, this);
+ return this;
+ },
+
+ invoke: function (methodName) {
+ var args = Array.prototype.slice.call(arguments, 1),
+ i, layer;
+
+ for (i in this._layers) {
+ if (this._layers.hasOwnProperty(i)) {
+ layer = this._layers[i];
+
+ if (layer[methodName]) {
+ layer[methodName].apply(layer, args);
+ }
+ }
+ }
+
+ return this;
+ },
+
+ onAdd: function (map) {
+ this._map = map;
+ this.eachLayer(map.addLayer, map);
+ },
+
+ onRemove: function (map) {
+ this.eachLayer(map.removeLayer, map);
+ this._map = null;
+ },
+
+ addTo: function (map) {
+ map.addLayer(this);
+ return this;
+ },
+
+ eachLayer: function (method, context) {
+ for (var i in this._layers) {
+ if (this._layers.hasOwnProperty(i)) {
+ method.call(context, this._layers[i]);
+ }
+ }
+ },
+
+ setZIndex: function (zIndex) {
+ return this.invoke('setZIndex', zIndex);
+ }
+});
+
+L.layerGroup = function (layers) {
+ return new L.LayerGroup(layers);
+};
+
+
+/*
+ * L.FeatureGroup extends L.LayerGroup by introducing mouse events and additional methods
+ * shared between a group of interactive layers (like vectors or markers).
+ */
+
+L.FeatureGroup = L.LayerGroup.extend({
+ includes: L.Mixin.Events,
+
+ statics: {
+ EVENTS: 'click dblclick mouseover mouseout mousemove contextmenu'
+ },
+
+ addLayer: function (layer) {
+ if (this._layers[L.stamp(layer)]) {
+ return this;
+ }
+
+ layer.on(L.FeatureGroup.EVENTS, this._propagateEvent, this);
+
+ L.LayerGroup.prototype.addLayer.call(this, layer);
+
+ if (this._popupContent && layer.bindPopup) {
+ layer.bindPopup(this._popupContent, this._popupOptions);
+ }
+
+ return this.fire('layeradd', {layer: layer});
+ },
+
+ removeLayer: function (layer) {
+ layer.off(L.FeatureGroup.EVENTS, this._propagateEvent, this);
+
+ L.LayerGroup.prototype.removeLayer.call(this, layer);
+
+
+ if (this._popupContent) {
+ this.invoke('unbindPopup');
+ }
+
+ return this.fire('layerremove', {layer: layer});
+ },
+
+ bindPopup: function (content, options) {
+ this._popupContent = content;
+ this._popupOptions = options;
+ return this.invoke('bindPopup', content, options);
+ },
+
+ setStyle: function (style) {
+ return this.invoke('setStyle', style);
+ },
+
+ bringToFront: function () {
+ return this.invoke('bringToFront');
+ },
+
+ bringToBack: function () {
+ return this.invoke('bringToBack');
+ },
+
+ getBounds: function () {
+ var bounds = new L.LatLngBounds();
+
+ this.eachLayer(function (layer) {
+ bounds.extend(layer instanceof L.Marker ? layer.getLatLng() : layer.getBounds());
+ });
+
+ return bounds;
+ },
+
+ _propagateEvent: function (e) {
+ e.layer = e.target;
+ e.target = this;
+
+ this.fire(e.type, e);
+ }
+});
+
+L.featureGroup = function (layers) {
+ return new L.FeatureGroup(layers);
+};
+
+
+/*
+ * L.Path is a base class for rendering vector paths on a map. Inherited by Polyline, Circle, etc.
+ */
+
+L.Path = L.Class.extend({
+ includes: [L.Mixin.Events],
+
+ statics: {
+ // how much to extend the clip area around the map view
+ // (relative to its size, e.g. 0.5 is half the screen in each direction)
+ // set it so that SVG element doesn't exceed 1280px (vectors flicker on dragend if it is)
+ CLIP_PADDING: L.Browser.mobile ?
+ Math.max(0, Math.min(0.5,
+ (1280 / Math.max(window.innerWidth, window.innerHeight) - 1) / 2)) : 0.5
+ },
+
+ options: {
+ stroke: true,
+ color: '#0033ff',
+ dashArray: null,
+ weight: 5,
+ opacity: 0.5,
+
+ fill: false,
+ fillColor: null, //same as color by default
+ fillOpacity: 0.2,
+
+ clickable: true
+ },
+
+ initialize: function (options) {
+ L.setOptions(this, options);
+ },
+
+ onAdd: function (map) {
+ this._map = map;
+
+ if (!this._container) {
+ this._initElements();
+ this._initEvents();
+ }
+
+ this.projectLatlngs();
+ this._updatePath();
+
+ if (this._container) {
+ this._map._pathRoot.appendChild(this._container);
+ }
+
+ this.fire('add');
+
+ map.on({
+ 'viewreset': this.projectLatlngs,
+ 'moveend': this._updatePath
+ }, this);
+ },
+
+ addTo: function (map) {
+ map.addLayer(this);
+ return this;
+ },
+
+ onRemove: function (map) {
+ map._pathRoot.removeChild(this._container);
+
+ // Need to fire remove event before we set _map to null as the event hooks might need the object
+ this.fire('remove');
+ this._map = null;
+
+ if (L.Browser.vml) {
+ this._container = null;
+ this._stroke = null;
+ this._fill = null;
+ }
+
+ map.off({
+ 'viewreset': this.projectLatlngs,
+ 'moveend': this._updatePath
+ }, this);
+ },
+
+ projectLatlngs: function () {
+ // do all projection stuff here
+ },
+
+ setStyle: function (style) {
+ L.setOptions(this, style);
+
+ if (this._container) {
+ this._updateStyle();
+ }
+
+ return this;
+ },
+
+ redraw: function () {
+ if (this._map) {
+ this.projectLatlngs();
+ this._updatePath();
+ }
+ return this;
+ }
+});
+
+L.Map.include({
+ _updatePathViewport: function () {
+ var p = L.Path.CLIP_PADDING,
+ size = this.getSize(),
+ panePos = L.DomUtil.getPosition(this._mapPane),
+ min = panePos.multiplyBy(-1)._subtract(size.multiplyBy(p)._round()),
+ max = min.add(size.multiplyBy(1 + p * 2)._round());
+
+ this._pathViewport = new L.Bounds(min, max);
+ }
+});
+
+
+/*
+ * Extends L.Path with SVG-specific rendering code.
+ */
+
+L.Path.SVG_NS = 'http://www.w3.org/2000/svg';
+
+L.Browser.svg = !!(document.createElementNS && document.createElementNS(L.Path.SVG_NS, 'svg').createSVGRect);
+
+L.Path = L.Path.extend({
+ statics: {
+ SVG: L.Browser.svg
+ },
+
+ bringToFront: function () {
+ var root = this._map._pathRoot,
+ path = this._container;
+
+ if (path && root.lastChild !== path) {
+ root.appendChild(path);
+ }
+ return this;
+ },
+
+ bringToBack: function () {
+ var root = this._map._pathRoot,
+ path = this._container,
+ first = root.firstChild;
+
+ if (path && first !== path) {
+ root.insertBefore(path, first);
+ }
+ return this;
+ },
+
+ getPathString: function () {
+ // form path string here
+ },
+
+ _createElement: function (name) {
+ return document.createElementNS(L.Path.SVG_NS, name);
+ },
+
+ _initElements: function () {
+ this._map._initPathRoot();
+ this._initPath();
+ this._initStyle();
+ },
+
+ _initPath: function () {
+ this._container = this._createElement('g');
+
+ this._path = this._createElement('path');
+ this._container.appendChild(this._path);
+ },
+
+ _initStyle: function () {
+ if (this.options.stroke) {
+ this._path.setAttribute('stroke-linejoin', 'round');
+ this._path.setAttribute('stroke-linecap', 'round');
+ }
+ if (this.options.fill) {
+ this._path.setAttribute('fill-rule', 'evenodd');
+ }
+ this._updateStyle();
+ },
+
+ _updateStyle: function () {
+ if (this.options.stroke) {
+ this._path.setAttribute('stroke', this.options.color);
+ this._path.setAttribute('stroke-opacity', this.options.opacity);
+ this._path.setAttribute('stroke-width', this.options.weight);
+ if (this.options.dashArray) {
+ this._path.setAttribute('stroke-dasharray', this.options.dashArray);
+ } else {
+ this._path.removeAttribute('stroke-dasharray');
+ }
+ } else {
+ this._path.setAttribute('stroke', 'none');
+ }
+ if (this.options.fill) {
+ this._path.setAttribute('fill', this.options.fillColor || this.options.color);
+ this._path.setAttribute('fill-opacity', this.options.fillOpacity);
+ } else {
+ this._path.setAttribute('fill', 'none');
+ }
+ },
+
+ _updatePath: function () {
+ var str = this.getPathString();
+ if (!str) {
+ // fix webkit empty string parsing bug
+ str = 'M0 0';
+ }
+ this._path.setAttribute('d', str);
+ },
+
+ // TODO remove duplication with L.Map
+ _initEvents: function () {
+ if (this.options.clickable) {
+ if (L.Browser.svg || !L.Browser.vml) {
+ this._path.setAttribute('class', 'leaflet-clickable');
+ }
+
+ L.DomEvent.on(this._container, 'click', this._onMouseClick, this);
+
+ var events = ['dblclick', 'mousedown', 'mouseover',
+ 'mouseout', 'mousemove', 'contextmenu'];
+ for (var i = 0; i < events.length; i++) {
+ L.DomEvent.on(this._container, events[i], this._fireMouseEvent, this);
+ }
+ }
+ },
+
+ _onMouseClick: function (e) {
+ if (this._map.dragging && this._map.dragging.moved()) { return; }
+
+ this._fireMouseEvent(e);
+ },
+
+ _fireMouseEvent: function (e) {
+ if (!this.hasEventListeners(e.type)) { return; }
+
+ var map = this._map,
+ containerPoint = map.mouseEventToContainerPoint(e),
+ layerPoint = map.containerPointToLayerPoint(containerPoint),
+ latlng = map.layerPointToLatLng(layerPoint);
+
+ this.fire(e.type, {
+ latlng: latlng,
+ layerPoint: layerPoint,
+ containerPoint: containerPoint,
+ originalEvent: e
+ });
+
+ if (e.type === 'contextmenu') {
+ L.DomEvent.preventDefault(e);
+ }
+ if (e.type !== 'mousemove') {
+ L.DomEvent.stopPropagation(e);
+ }
+ }
+});
+
+L.Map.include({
+ _initPathRoot: function () {
+ if (!this._pathRoot) {
+ this._pathRoot = L.Path.prototype._createElement('svg');
+ this._panes.overlayPane.appendChild(this._pathRoot);
+
+ if (this.options.zoomAnimation && L.Browser.any3d) {
+ this._pathRoot.setAttribute('class', ' leaflet-zoom-animated');
+
+ this.on({
+ 'zoomanim': this._animatePathZoom,
+ 'zoomend': this._endPathZoom
+ });
+ } else {
+ this._pathRoot.setAttribute('class', ' leaflet-zoom-hide');
+ }
+
+ this.on('moveend', this._updateSvgViewport);
+ this._updateSvgViewport();
+ }
+ },
+
+ _animatePathZoom: function (e) {
+ var scale = this.getZoomScale(e.zoom),
+ offset = this._getCenterOffset(e.center)._multiplyBy(-scale)._add(this._pathViewport.min);
+
+ this._pathRoot.style[L.DomUtil.TRANSFORM] =
+ L.DomUtil.getTranslateString(offset) + ' scale(' + scale + ') ';
+
+ this._pathZooming = true;
+ },
+
+ _endPathZoom: function () {
+ this._pathZooming = false;
+ },
+
+ _updateSvgViewport: function () {
+
+ if (this._pathZooming) {
+ // Do not update SVGs while a zoom animation is going on otherwise the animation will break.
+ // When the zoom animation ends we will be updated again anyway
+ // This fixes the case where you do a momentum move and zoom while the move is still ongoing.
+ return;
+ }
+
+ this._updatePathViewport();
+
+ var vp = this._pathViewport,
+ min = vp.min,
+ max = vp.max,
+ width = max.x - min.x,
+ height = max.y - min.y,
+ root = this._pathRoot,
+ pane = this._panes.overlayPane;
+
+ // Hack to make flicker on drag end on mobile webkit less irritating
+ if (L.Browser.mobileWebkit) {
+ pane.removeChild(root);
+ }
+
+ L.DomUtil.setPosition(root, min);
+ root.setAttribute('width', width);
+ root.setAttribute('height', height);
+ root.setAttribute('viewBox', [min.x, min.y, width, height].join(' '));
+
+ if (L.Browser.mobileWebkit) {
+ pane.appendChild(root);
+ }
+ }
+});
+
+
+/*
+ * Popup extension to L.Path (polylines, polygons, circles), adding popup-related methods.
+ */
+
+L.Path.include({
+
+ bindPopup: function (content, options) {
+
+ if (!this._popup || options) {
+ this._popup = new L.Popup(options, this);
+ }
+
+ this._popup.setContent(content);
+
+ if (!this._popupHandlersAdded) {
+ this
+ .on('click', this._openPopup, this)
+ .on('remove', this.closePopup, this);
+
+ this._popupHandlersAdded = true;
+ }
+
+ return this;
+ },
+
+ unbindPopup: function () {
+ if (this._popup) {
+ this._popup = null;
+ this
+ .off('click', this._openPopup)
+ .off('remove', this.closePopup);
+
+ this._popupHandlersAdded = false;
+ }
+ return this;
+ },
+
+ openPopup: function (latlng) {
+
+ if (this._popup) {
+ // open the popup from one of the path's points if not specified
+ latlng = latlng || this._latlng ||
+ this._latlngs[Math.floor(this._latlngs.length / 2)];
+
+ this._openPopup({latlng: latlng});
+ }
+
+ return this;
+ },
+
+ closePopup: function () {
+ if (this._popup) {
+ this._popup._close();
+ }
+ return this;
+ },
+
+ _openPopup: function (e) {
+ this._popup.setLatLng(e.latlng);
+ this._map.openPopup(this._popup);
+ }
+});
+
+
+/*
+ * Vector rendering for IE6-8 through VML.
+ * Thanks to Dmitry Baranovsky and his Raphael library for inspiration!
+ */
+
+L.Browser.vml = !L.Browser.svg && (function () {
+ try {
+ var div = document.createElement('div');
+ div.innerHTML = ' ';
+
+ var shape = div.firstChild;
+ shape.style.behavior = 'url(#default#VML)';
+
+ return shape && (typeof shape.adj === 'object');
+
+ } catch (e) {
+ return false;
+ }
+}());
+
+L.Path = L.Browser.svg || !L.Browser.vml ? L.Path : L.Path.extend({
+ statics: {
+ VML: true,
+ CLIP_PADDING: 0.02
+ },
+
+ _createElement: (function () {
+ try {
+ document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml');
+ return function (name) {
+ return document.createElement('');
+ };
+ } catch (e) {
+ return function (name) {
+ return document.createElement(
+ '<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">');
+ };
+ }
+ }()),
+
+ _initPath: function () {
+ var container = this._container = this._createElement('shape');
+ L.DomUtil.addClass(container, 'leaflet-vml-shape');
+ if (this.options.clickable) {
+ L.DomUtil.addClass(container, 'leaflet-clickable');
+ }
+ container.coordsize = '1 1';
+
+ this._path = this._createElement('path');
+ container.appendChild(this._path);
+
+ this._map._pathRoot.appendChild(container);
+ },
+
+ _initStyle: function () {
+ this._updateStyle();
+ },
+
+ _updateStyle: function () {
+ var stroke = this._stroke,
+ fill = this._fill,
+ options = this.options,
+ container = this._container;
+
+ container.stroked = options.stroke;
+ container.filled = options.fill;
+
+ if (options.stroke) {
+ if (!stroke) {
+ stroke = this._stroke = this._createElement('stroke');
+ stroke.endcap = 'round';
+ container.appendChild(stroke);
+ }
+ stroke.weight = options.weight + 'px';
+ stroke.color = options.color;
+ stroke.opacity = options.opacity;
+
+ if (options.dashArray) {
+ stroke.dashStyle = options.dashArray instanceof Array ?
+ options.dashArray.join(' ') :
+ options.dashArray.replace(/ *, */g, ' ');
+ } else {
+ stroke.dashStyle = '';
+ }
+
+ } else if (stroke) {
+ container.removeChild(stroke);
+ this._stroke = null;
+ }
+
+ if (options.fill) {
+ if (!fill) {
+ fill = this._fill = this._createElement('fill');
+ container.appendChild(fill);
+ }
+ fill.color = options.fillColor || options.color;
+ fill.opacity = options.fillOpacity;
+
+ } else if (fill) {
+ container.removeChild(fill);
+ this._fill = null;
+ }
+ },
+
+ _updatePath: function () {
+ var style = this._container.style;
+
+ style.display = 'none';
+ this._path.v = this.getPathString() + ' '; // the space fixes IE empty path string bug
+ style.display = '';
+ }
+});
+
+L.Map.include(L.Browser.svg || !L.Browser.vml ? {} : {
+ _initPathRoot: function () {
+ if (this._pathRoot) { return; }
+
+ var root = this._pathRoot = document.createElement('div');
+ root.className = 'leaflet-vml-container';
+ this._panes.overlayPane.appendChild(root);
+
+ this.on('moveend', this._updatePathViewport);
+ this._updatePathViewport();
+ }
+});
+
+
+/*
+ * Vector rendering for all browsers that support canvas.
+ */
+
+L.Browser.canvas = (function () {
+ return !!document.createElement('canvas').getContext;
+}());
+
+L.Path = (L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? L.Path : L.Path.extend({
+ statics: {
+ //CLIP_PADDING: 0.02, // not sure if there's a need to set it to a small value
+ CANVAS: true,
+ SVG: false
+ },
+
+ redraw: function () {
+ if (this._map) {
+ this.projectLatlngs();
+ this._requestUpdate();
+ }
+ return this;
+ },
+
+ setStyle: function (style) {
+ L.setOptions(this, style);
+
+ if (this._map) {
+ this._updateStyle();
+ this._requestUpdate();
+ }
+ return this;
+ },
+
+ onRemove: function (map) {
+ map
+ .off('viewreset', this.projectLatlngs, this)
+ .off('moveend', this._updatePath, this);
+
+ if (this.options.clickable) {
+ this._map.off('click', this._onClick, this);
+ }
+
+ this._requestUpdate();
+
+ this._map = null;
+ },
+
+ _requestUpdate: function () {
+ if (this._map && !L.Path._updateRequest) {
+ L.Path._updateRequest = L.Util.requestAnimFrame(this._fireMapMoveEnd, this._map);
+ }
+ },
+
+ _fireMapMoveEnd: function () {
+ L.Path._updateRequest = null;
+ this.fire('moveend');
+ },
+
+ _initElements: function () {
+ this._map._initPathRoot();
+ this._ctx = this._map._canvasCtx;
+ },
+
+ _updateStyle: function () {
+ var options = this.options;
+
+ if (options.stroke) {
+ this._ctx.lineWidth = options.weight;
+ this._ctx.strokeStyle = options.color;
+ }
+ if (options.fill) {
+ this._ctx.fillStyle = options.fillColor || options.color;
+ }
+ },
+
+ _drawPath: function () {
+ var i, j, len, len2, point, drawMethod;
+
+ this._ctx.beginPath();
+
+ for (i = 0, len = this._parts.length; i < len; i++) {
+ for (j = 0, len2 = this._parts[i].length; j < len2; j++) {
+ point = this._parts[i][j];
+ drawMethod = (j === 0 ? 'move' : 'line') + 'To';
+
+ this._ctx[drawMethod](point.x, point.y);
+ }
+ // TODO refactor ugly hack
+ if (this instanceof L.Polygon) {
+ this._ctx.closePath();
+ }
+ }
+ },
+
+ _checkIfEmpty: function () {
+ return !this._parts.length;
+ },
+
+ _updatePath: function () {
+ if (this._checkIfEmpty()) { return; }
+
+ var ctx = this._ctx,
+ options = this.options;
+
+ this._drawPath();
+ ctx.save();
+ this._updateStyle();
+
+ if (options.fill) {
+ ctx.globalAlpha = options.fillOpacity;
+ ctx.fill();
+ }
+
+ if (options.stroke) {
+ ctx.globalAlpha = options.opacity;
+ ctx.stroke();
+ }
+
+ ctx.restore();
+
+ // TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature
+ },
+
+ _initEvents: function () {
+ if (this.options.clickable) {
+ // TODO hand cursor
+ // TODO mouseover, mouseout, dblclick
+ this._map.on('click', this._onClick, this);
+ }
+ },
+
+ _onClick: function (e) {
+ if (this._containsPoint(e.layerPoint)) {
+ this.fire('click', {
+ latlng: e.latlng,
+ layerPoint: e.layerPoint,
+ containerPoint: e.containerPoint,
+ originalEvent: e
+ });
+ }
+ }
+});
+
+L.Map.include((L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? {} : {
+ _initPathRoot: function () {
+ var root = this._pathRoot,
+ ctx;
+
+ if (!root) {
+ root = this._pathRoot = document.createElement("canvas");
+ root.style.position = 'absolute';
+ ctx = this._canvasCtx = root.getContext('2d');
+
+ ctx.lineCap = "round";
+ ctx.lineJoin = "round";
+
+ this._panes.overlayPane.appendChild(root);
+
+ if (this.options.zoomAnimation) {
+ this._pathRoot.className = 'leaflet-zoom-animated';
+ this.on('zoomanim', this._animatePathZoom);
+ this.on('zoomend', this._endPathZoom);
+ }
+ this.on('moveend', this._updateCanvasViewport);
+ this._updateCanvasViewport();
+ }
+ },
+
+ _updateCanvasViewport: function () {
+ // don't redraw while zooming. See _updateSvgViewport for more details
+ if (this._pathZooming) { return; }
+ this._updatePathViewport();
+
+ var vp = this._pathViewport,
+ min = vp.min,
+ size = vp.max.subtract(min),
+ root = this._pathRoot;
+
+ //TODO check if this works properly on mobile webkit
+ L.DomUtil.setPosition(root, min);
+ root.width = size.x;
+ root.height = size.y;
+ root.getContext('2d').translate(-min.x, -min.y);
+ }
+});
+
+
+/*
+ * L.LineUtil contains different utility functions for line segments
+ * and polylines (clipping, simplification, distances, etc.)
+ */
+
+/*jshint bitwise:false */ // allow bitwise oprations for this file
+
+L.LineUtil = {
+
+ // Simplify polyline with vertex reduction and Douglas-Peucker simplification.
+ // Improves rendering performance dramatically by lessening the number of points to draw.
+
+ simplify: function (/*Point[]*/ points, /*Number*/ tolerance) {
+ if (!tolerance || !points.length) {
+ return points.slice();
+ }
+
+ var sqTolerance = tolerance * tolerance;
+
+ // stage 1: vertex reduction
+ points = this._reducePoints(points, sqTolerance);
+
+ // stage 2: Douglas-Peucker simplification
+ points = this._simplifyDP(points, sqTolerance);
+
+ return points;
+ },
+
+ // distance from a point to a segment between two points
+ pointToSegmentDistance: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) {
+ return Math.sqrt(this._sqClosestPointOnSegment(p, p1, p2, true));
+ },
+
+ closestPointOnSegment: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) {
+ return this._sqClosestPointOnSegment(p, p1, p2);
+ },
+
+ // Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm
+ _simplifyDP: function (points, sqTolerance) {
+
+ var len = points.length,
+ ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array,
+ markers = new ArrayConstructor(len);
+
+ markers[0] = markers[len - 1] = 1;
+
+ this._simplifyDPStep(points, markers, sqTolerance, 0, len - 1);
+
+ var i,
+ newPoints = [];
+
+ for (i = 0; i < len; i++) {
+ if (markers[i]) {
+ newPoints.push(points[i]);
+ }
+ }
+
+ return newPoints;
+ },
+
+ _simplifyDPStep: function (points, markers, sqTolerance, first, last) {
+
+ var maxSqDist = 0,
+ index, i, sqDist;
+
+ for (i = first + 1; i <= last - 1; i++) {
+ sqDist = this._sqClosestPointOnSegment(points[i], points[first], points[last], true);
+
+ if (sqDist > maxSqDist) {
+ index = i;
+ maxSqDist = sqDist;
+ }
+ }
+
+ if (maxSqDist > sqTolerance) {
+ markers[index] = 1;
+
+ this._simplifyDPStep(points, markers, sqTolerance, first, index);
+ this._simplifyDPStep(points, markers, sqTolerance, index, last);
+ }
+ },
+
+ // reduce points that are too close to each other to a single point
+ _reducePoints: function (points, sqTolerance) {
+ var reducedPoints = [points[0]];
+
+ for (var i = 1, prev = 0, len = points.length; i < len; i++) {
+ if (this._sqDist(points[i], points[prev]) > sqTolerance) {
+ reducedPoints.push(points[i]);
+ prev = i;
+ }
+ }
+ if (prev < len - 1) {
+ reducedPoints.push(points[len - 1]);
+ }
+ return reducedPoints;
+ },
+
+ // Cohen-Sutherland line clipping algorithm.
+ // Used to avoid rendering parts of a polyline that are not currently visible.
+
+ clipSegment: function (a, b, bounds, useLastCode) {
+ var codeA = useLastCode ? this._lastCode : this._getBitCode(a, bounds),
+ codeB = this._getBitCode(b, bounds),
+
+ codeOut, p, newCode;
+
+ // save 2nd code to avoid calculating it on the next segment
+ this._lastCode = codeB;
+
+ while (true) {
+ // if a,b is inside the clip window (trivial accept)
+ if (!(codeA | codeB)) {
+ return [a, b];
+ // if a,b is outside the clip window (trivial reject)
+ } else if (codeA & codeB) {
+ return false;
+ // other cases
+ } else {
+ codeOut = codeA || codeB,
+ p = this._getEdgeIntersection(a, b, codeOut, bounds),
+ newCode = this._getBitCode(p, bounds);
+
+ if (codeOut === codeA) {
+ a = p;
+ codeA = newCode;
+ } else {
+ b = p;
+ codeB = newCode;
+ }
+ }
+ }
+ },
+
+ _getEdgeIntersection: function (a, b, code, bounds) {
+ var dx = b.x - a.x,
+ dy = b.y - a.y,
+ min = bounds.min,
+ max = bounds.max;
+
+ if (code & 8) { // top
+ return new L.Point(a.x + dx * (max.y - a.y) / dy, max.y);
+ } else if (code & 4) { // bottom
+ return new L.Point(a.x + dx * (min.y - a.y) / dy, min.y);
+ } else if (code & 2) { // right
+ return new L.Point(max.x, a.y + dy * (max.x - a.x) / dx);
+ } else if (code & 1) { // left
+ return new L.Point(min.x, a.y + dy * (min.x - a.x) / dx);
+ }
+ },
+
+ _getBitCode: function (/*Point*/ p, bounds) {
+ var code = 0;
+
+ if (p.x < bounds.min.x) { // left
+ code |= 1;
+ } else if (p.x > bounds.max.x) { // right
+ code |= 2;
+ }
+ if (p.y < bounds.min.y) { // bottom
+ code |= 4;
+ } else if (p.y > bounds.max.y) { // top
+ code |= 8;
+ }
+
+ return code;
+ },
+
+ // square distance (to avoid unnecessary Math.sqrt calls)
+ _sqDist: function (p1, p2) {
+ var dx = p2.x - p1.x,
+ dy = p2.y - p1.y;
+ return dx * dx + dy * dy;
+ },
+
+ // return closest point on segment or distance to that point
+ _sqClosestPointOnSegment: function (p, p1, p2, sqDist) {
+ var x = p1.x,
+ y = p1.y,
+ dx = p2.x - x,
+ dy = p2.y - y,
+ dot = dx * dx + dy * dy,
+ t;
+
+ if (dot > 0) {
+ t = ((p.x - x) * dx + (p.y - y) * dy) / dot;
+
+ if (t > 1) {
+ x = p2.x;
+ y = p2.y;
+ } else if (t > 0) {
+ x += dx * t;
+ y += dy * t;
+ }
+ }
+
+ dx = p.x - x;
+ dy = p.y - y;
+
+ return sqDist ? dx * dx + dy * dy : new L.Point(x, y);
+ }
+};
+
+
+/*
+ * L.Polygon is used to display polylines on a map.
+ */
+
+L.Polyline = L.Path.extend({
+ initialize: function (latlngs, options) {
+ L.Path.prototype.initialize.call(this, options);
+
+ this._latlngs = this._convertLatLngs(latlngs);
+ },
+
+ options: {
+ // how much to simplify the polyline on each zoom level
+ // more = better performance and smoother look, less = more accurate
+ smoothFactor: 1.0,
+ noClip: false
+ },
+
+ projectLatlngs: function () {
+ this._originalPoints = [];
+
+ for (var i = 0, len = this._latlngs.length; i < len; i++) {
+ this._originalPoints[i] = this._map.latLngToLayerPoint(this._latlngs[i]);
+ }
+ },
+
+ getPathString: function () {
+ for (var i = 0, len = this._parts.length, str = ''; i < len; i++) {
+ str += this._getPathPartStr(this._parts[i]);
+ }
+ return str;
+ },
+
+ getLatLngs: function () {
+ return this._latlngs;
+ },
+
+ setLatLngs: function (latlngs) {
+ this._latlngs = this._convertLatLngs(latlngs);
+ return this.redraw();
+ },
+
+ addLatLng: function (latlng) {
+ this._latlngs.push(L.latLng(latlng));
+ return this.redraw();
+ },
+
+ spliceLatLngs: function () { // (Number index, Number howMany)
+ var removed = [].splice.apply(this._latlngs, arguments);
+ this._convertLatLngs(this._latlngs);
+ this.redraw();
+ return removed;
+ },
+
+ closestLayerPoint: function (p) {
+ var minDistance = Infinity, parts = this._parts, p1, p2, minPoint = null;
+
+ for (var j = 0, jLen = parts.length; j < jLen; j++) {
+ var points = parts[j];
+ for (var i = 1, len = points.length; i < len; i++) {
+ p1 = points[i - 1];
+ p2 = points[i];
+ var sqDist = L.LineUtil._sqClosestPointOnSegment(p, p1, p2, true);
+ if (sqDist < minDistance) {
+ minDistance = sqDist;
+ minPoint = L.LineUtil._sqClosestPointOnSegment(p, p1, p2);
+ }
+ }
+ }
+ if (minPoint) {
+ minPoint.distance = Math.sqrt(minDistance);
+ }
+ return minPoint;
+ },
+
+ getBounds: function () {
+ var bounds = new L.LatLngBounds(),
+ latLngs = this.getLatLngs(),
+ i, len;
+
+ for (i = 0, len = latLngs.length; i < len; i++) {
+ bounds.extend(latLngs[i]);
+ }
+
+ return bounds;
+ },
+
+ _convertLatLngs: function (latlngs) {
+ var i, len;
+ for (i = 0, len = latlngs.length; i < len; i++) {
+ if (L.Util.isArray(latlngs[i]) && typeof latlngs[i][0] !== 'number') {
+ return;
+ }
+ latlngs[i] = L.latLng(latlngs[i]);
+ }
+ return latlngs;
+ },
+
+ _initEvents: function () {
+ L.Path.prototype._initEvents.call(this);
+ },
+
+ _getPathPartStr: function (points) {
+ var round = L.Path.VML;
+
+ for (var j = 0, len2 = points.length, str = '', p; j < len2; j++) {
+ p = points[j];
+ if (round) {
+ p._round();
+ }
+ str += (j ? 'L' : 'M') + p.x + ' ' + p.y;
+ }
+ return str;
+ },
+
+ _clipPoints: function () {
+ var points = this._originalPoints,
+ len = points.length,
+ i, k, segment;
+
+ if (this.options.noClip) {
+ this._parts = [points];
+ return;
+ }
+
+ this._parts = [];
+
+ var parts = this._parts,
+ vp = this._map._pathViewport,
+ lu = L.LineUtil;
+
+ for (i = 0, k = 0; i < len - 1; i++) {
+ segment = lu.clipSegment(points[i], points[i + 1], vp, i);
+ if (!segment) {
+ continue;
+ }
+
+ parts[k] = parts[k] || [];
+ parts[k].push(segment[0]);
+
+ // if segment goes out of screen, or it's the last one, it's the end of the line part
+ if ((segment[1] !== points[i + 1]) || (i === len - 2)) {
+ parts[k].push(segment[1]);
+ k++;
+ }
+ }
+ },
+
+ // simplify each clipped part of the polyline
+ _simplifyPoints: function () {
+ var parts = this._parts,
+ lu = L.LineUtil;
+
+ for (var i = 0, len = parts.length; i < len; i++) {
+ parts[i] = lu.simplify(parts[i], this.options.smoothFactor);
+ }
+ },
+
+ _updatePath: function () {
+ if (!this._map) { return; }
+
+ this._clipPoints();
+ this._simplifyPoints();
+
+ L.Path.prototype._updatePath.call(this);
+ }
+});
+
+L.polyline = function (latlngs, options) {
+ return new L.Polyline(latlngs, options);
+};
+
+
+/*
+ * L.PolyUtil contains utility functions for polygons (clipping, etc.).
+ */
+
+/*jshint bitwise:false */ // allow bitwise operations here
+
+L.PolyUtil = {};
+
+/*
+ * Sutherland-Hodgeman polygon clipping algorithm.
+ * Used to avoid rendering parts of a polygon that are not currently visible.
+ */
+L.PolyUtil.clipPolygon = function (points, bounds) {
+ var clippedPoints,
+ edges = [1, 4, 2, 8],
+ i, j, k,
+ a, b,
+ len, edge, p,
+ lu = L.LineUtil;
+
+ for (i = 0, len = points.length; i < len; i++) {
+ points[i]._code = lu._getBitCode(points[i], bounds);
+ }
+
+ // for each edge (left, bottom, right, top)
+ for (k = 0; k < 4; k++) {
+ edge = edges[k];
+ clippedPoints = [];
+
+ for (i = 0, len = points.length, j = len - 1; i < len; j = i++) {
+ a = points[i];
+ b = points[j];
+
+ // if a is inside the clip window
+ if (!(a._code & edge)) {
+ // if b is outside the clip window (a->b goes out of screen)
+ if (b._code & edge) {
+ p = lu._getEdgeIntersection(b, a, edge, bounds);
+ p._code = lu._getBitCode(p, bounds);
+ clippedPoints.push(p);
+ }
+ clippedPoints.push(a);
+
+ // else if b is inside the clip window (a->b enters the screen)
+ } else if (!(b._code & edge)) {
+ p = lu._getEdgeIntersection(b, a, edge, bounds);
+ p._code = lu._getBitCode(p, bounds);
+ clippedPoints.push(p);
+ }
+ }
+ points = clippedPoints;
+ }
+
+ return points;
+};
+
+
+/*
+ * L.Polygon is used to display polygons on a map.
+ */
+
+L.Polygon = L.Polyline.extend({
+ options: {
+ fill: true
+ },
+
+ initialize: function (latlngs, options) {
+ L.Polyline.prototype.initialize.call(this, latlngs, options);
+
+ if (latlngs && L.Util.isArray(latlngs[0]) && (typeof latlngs[0][0] !== 'number')) {
+ this._latlngs = this._convertLatLngs(latlngs[0]);
+ this._holes = latlngs.slice(1);
+ }
+ },
+
+ projectLatlngs: function () {
+ L.Polyline.prototype.projectLatlngs.call(this);
+
+ // project polygon holes points
+ // TODO move this logic to Polyline to get rid of duplication
+ this._holePoints = [];
+
+ if (!this._holes) { return; }
+
+ var i, j, len, len2;
+
+ for (i = 0, len = this._holes.length; i < len; i++) {
+ this._holePoints[i] = [];
+
+ for (j = 0, len2 = this._holes[i].length; j < len2; j++) {
+ this._holePoints[i][j] = this._map.latLngToLayerPoint(this._holes[i][j]);
+ }
+ }
+ },
+
+ _clipPoints: function () {
+ var points = this._originalPoints,
+ newParts = [];
+
+ this._parts = [points].concat(this._holePoints);
+
+ if (this.options.noClip) { return; }
+
+ for (var i = 0, len = this._parts.length; i < len; i++) {
+ var clipped = L.PolyUtil.clipPolygon(this._parts[i], this._map._pathViewport);
+ if (clipped.length) {
+ newParts.push(clipped);
+ }
+ }
+
+ this._parts = newParts;
+ },
+
+ _getPathPartStr: function (points) {
+ var str = L.Polyline.prototype._getPathPartStr.call(this, points);
+ return str + (L.Browser.svg ? 'z' : 'x');
+ }
+});
+
+L.polygon = function (latlngs, options) {
+ return new L.Polygon(latlngs, options);
+};
+
+
+/*
+ * Contains L.MultiPolyline and L.MultiPolygon layers.
+ */
+
+(function () {
+ function createMulti(Klass) {
+
+ return L.FeatureGroup.extend({
+
+ initialize: function (latlngs, options) {
+ this._layers = {};
+ this._options = options;
+ this.setLatLngs(latlngs);
+ },
+
+ setLatLngs: function (latlngs) {
+ var i = 0,
+ len = latlngs.length;
+
+ this.eachLayer(function (layer) {
+ if (i < len) {
+ layer.setLatLngs(latlngs[i++]);
+ } else {
+ this.removeLayer(layer);
+ }
+ }, this);
+
+ while (i < len) {
+ this.addLayer(new Klass(latlngs[i++], this._options));
+ }
+
+ return this;
+ }
+ });
+ }
+
+ L.MultiPolyline = createMulti(L.Polyline);
+ L.MultiPolygon = createMulti(L.Polygon);
+
+ L.multiPolyline = function (latlngs, options) {
+ return new L.MultiPolyline(latlngs, options);
+ };
+
+ L.multiPolygon = function (latlngs, options) {
+ return new L.MultiPolygon(latlngs, options);
+ };
+}());
+
+
+/*
+ * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object.
+ */
+
+L.Rectangle = L.Polygon.extend({
+ initialize: function (latLngBounds, options) {
+ L.Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options);
+ },
+
+ setBounds: function (latLngBounds) {
+ this.setLatLngs(this._boundsToLatLngs(latLngBounds));
+ },
+
+ _boundsToLatLngs: function (latLngBounds) {
+ latLngBounds = L.latLngBounds(latLngBounds);
+ return [
+ latLngBounds.getSouthWest(),
+ latLngBounds.getNorthWest(),
+ latLngBounds.getNorthEast(),
+ latLngBounds.getSouthEast()
+ ];
+ }
+});
+
+L.rectangle = function (latLngBounds, options) {
+ return new L.Rectangle(latLngBounds, options);
+};
+
+
+/*
+ * L.Circle is a circle overlay (with a certain radius in meters).
+ */
+
+L.Circle = L.Path.extend({
+ initialize: function (latlng, radius, options) {
+ L.Path.prototype.initialize.call(this, options);
+
+ this._latlng = L.latLng(latlng);
+ this._mRadius = radius;
+ },
+
+ options: {
+ fill: true
+ },
+
+ setLatLng: function (latlng) {
+ this._latlng = L.latLng(latlng);
+ return this.redraw();
+ },
+
+ setRadius: function (radius) {
+ this._mRadius = radius;
+ return this.redraw();
+ },
+
+ projectLatlngs: function () {
+ var lngRadius = this._getLngRadius(),
+ latlng2 = new L.LatLng(this._latlng.lat, this._latlng.lng - lngRadius),
+ point2 = this._map.latLngToLayerPoint(latlng2);
+
+ this._point = this._map.latLngToLayerPoint(this._latlng);
+ this._radius = Math.max(Math.round(this._point.x - point2.x), 1);
+ },
+
+ getBounds: function () {
+ var lngRadius = this._getLngRadius(),
+ latRadius = (this._mRadius / 40075017) * 360,
+ latlng = this._latlng,
+ sw = new L.LatLng(latlng.lat - latRadius, latlng.lng - lngRadius),
+ ne = new L.LatLng(latlng.lat + latRadius, latlng.lng + lngRadius);
+
+ return new L.LatLngBounds(sw, ne);
+ },
+
+ getLatLng: function () {
+ return this._latlng;
+ },
+
+ getPathString: function () {
+ var p = this._point,
+ r = this._radius;
+
+ if (this._checkIfEmpty()) {
+ return '';
+ }
+
+ if (L.Browser.svg) {
+ return "M" + p.x + "," + (p.y - r) +
+ "A" + r + "," + r + ",0,1,1," +
+ (p.x - 0.1) + "," + (p.y - r) + " z";
+ } else {
+ p._round();
+ r = Math.round(r);
+ return "AL " + p.x + "," + p.y + " " + r + "," + r + " 0," + (65535 * 360);
+ }
+ },
+
+ getRadius: function () {
+ return this._mRadius;
+ },
+
+ // TODO Earth hardcoded, move into projection code!
+
+ _getLatRadius: function () {
+ return (this._mRadius / 40075017) * 360;
+ },
+
+ _getLngRadius: function () {
+ return this._getLatRadius() / Math.cos(L.LatLng.DEG_TO_RAD * this._latlng.lat);
+ },
+
+ _checkIfEmpty: function () {
+ if (!this._map) {
+ return false;
+ }
+ var vp = this._map._pathViewport,
+ r = this._radius,
+ p = this._point;
+
+ return p.x - r > vp.max.x || p.y - r > vp.max.y ||
+ p.x + r < vp.min.x || p.y + r < vp.min.y;
+ }
+});
+
+L.circle = function (latlng, radius, options) {
+ return new L.Circle(latlng, radius, options);
+};
+
+
+/*
+ * L.CircleMarker is a circle overlay with a permanent pixel radius.
+ */
+
+L.CircleMarker = L.Circle.extend({
+ options: {
+ radius: 10,
+ weight: 2
+ },
+
+ initialize: function (latlng, options) {
+ L.Circle.prototype.initialize.call(this, latlng, null, options);
+ this._radius = this.options.radius;
+ },
+
+ projectLatlngs: function () {
+ this._point = this._map.latLngToLayerPoint(this._latlng);
+ },
+
+ _updateStyle : function () {
+ L.Circle.prototype._updateStyle.call(this);
+ this.setRadius(this.options.radius);
+ },
+
+ setRadius: function (radius) {
+ this.options.radius = this._radius = radius;
+ return this.redraw();
+ }
+});
+
+L.circleMarker = function (latlng, options) {
+ return new L.CircleMarker(latlng, options);
+};
+
+
+/*
+ * Extends L.Polyline to be able to manually detect clicks on Canvas-rendered polylines.
+ */
+
+L.Polyline.include(!L.Path.CANVAS ? {} : {
+ _containsPoint: function (p, closed) {
+ var i, j, k, len, len2, dist, part,
+ w = this.options.weight / 2;
+
+ if (L.Browser.touch) {
+ w += 10; // polyline click tolerance on touch devices
+ }
+
+ for (i = 0, len = this._parts.length; i < len; i++) {
+ part = this._parts[i];
+ for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
+ if (!closed && (j === 0)) {
+ continue;
+ }
+
+ dist = L.LineUtil.pointToSegmentDistance(p, part[k], part[j]);
+
+ if (dist <= w) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+});
+
+
+/*
+ * Extends L.Polygon to be able to manually detect clicks on Canvas-rendered polygons.
+ */
+
+L.Polygon.include(!L.Path.CANVAS ? {} : {
+ _containsPoint: function (p) {
+ var inside = false,
+ part, p1, p2,
+ i, j, k,
+ len, len2;
+
+ // TODO optimization: check if within bounds first
+
+ if (L.Polyline.prototype._containsPoint.call(this, p, true)) {
+ // click on polygon border
+ return true;
+ }
+
+ // ray casting algorithm for detecting if point is in polygon
+
+ for (i = 0, len = this._parts.length; i < len; i++) {
+ part = this._parts[i];
+
+ for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
+ p1 = part[j];
+ p2 = part[k];
+
+ if (((p1.y > p.y) !== (p2.y > p.y)) &&
+ (p.x < (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x)) {
+ inside = !inside;
+ }
+ }
+ }
+
+ return inside;
+ }
+});
+
+
+/*
+ * Extends L.Circle with Canvas-specific code.
+ */
+
+L.Circle.include(!L.Path.CANVAS ? {} : {
+ _drawPath: function () {
+ var p = this._point;
+ this._ctx.beginPath();
+ this._ctx.arc(p.x, p.y, this._radius, 0, Math.PI * 2, false);
+ },
+
+ _containsPoint: function (p) {
+ var center = this._point,
+ w2 = this.options.stroke ? this.options.weight / 2 : 0;
+
+ return (p.distanceTo(center) <= this._radius + w2);
+ }
+});
+
+
+/*
+ * L.GeoJSON turns any GeoJSON data into a Leaflet layer.
+ */
+
+L.GeoJSON = L.FeatureGroup.extend({
+
+ initialize: function (geojson, options) {
+ L.setOptions(this, options);
+
+ this._layers = {};
+
+ if (geojson) {
+ this.addData(geojson);
+ }
+ },
+
+ addData: function (geojson) {
+ var features = L.Util.isArray(geojson) ? geojson : geojson.features,
+ i, len;
+
+ if (features) {
+ for (i = 0, len = features.length; i < len; i++) {
+ // Only add this if geometry or geometries are set and not null
+ if (features[i].geometries || features[i].geometry || features[i].features) {
+ this.addData(features[i]);
+ }
+ }
+ return this;
+ }
+
+ var options = this.options;
+
+ if (options.filter && !options.filter(geojson)) { return; }
+
+ var layer = L.GeoJSON.geometryToLayer(geojson, options.pointToLayer);
+ layer.feature = geojson;
+
+ layer.defaultOptions = layer.options;
+ this.resetStyle(layer);
+
+ if (options.onEachFeature) {
+ options.onEachFeature(geojson, layer);
+ }
+
+ return this.addLayer(layer);
+ },
+
+ resetStyle: function (layer) {
+ var style = this.options.style;
+ if (style) {
+ // reset any custom styles
+ L.Util.extend(layer.options, layer.defaultOptions);
+
+ this._setLayerStyle(layer, style);
+ }
+ },
+
+ setStyle: function (style) {
+ this.eachLayer(function (layer) {
+ this._setLayerStyle(layer, style);
+ }, this);
+ },
+
+ _setLayerStyle: function (layer, style) {
+ if (typeof style === 'function') {
+ style = style(layer.feature);
+ }
+ if (layer.setStyle) {
+ layer.setStyle(style);
+ }
+ }
+});
+
+L.extend(L.GeoJSON, {
+ geometryToLayer: function (geojson, pointToLayer) {
+ var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson,
+ coords = geometry.coordinates,
+ layers = [],
+ latlng, latlngs, i, len, layer;
+
+ switch (geometry.type) {
+ case 'Point':
+ latlng = this.coordsToLatLng(coords);
+ return pointToLayer ? pointToLayer(geojson, latlng) : new L.Marker(latlng);
+
+ case 'MultiPoint':
+ for (i = 0, len = coords.length; i < len; i++) {
+ latlng = this.coordsToLatLng(coords[i]);
+ layer = pointToLayer ? pointToLayer(geojson, latlng) : new L.Marker(latlng);
+ layers.push(layer);
+ }
+ return new L.FeatureGroup(layers);
+
+ case 'LineString':
+ latlngs = this.coordsToLatLngs(coords);
+ return new L.Polyline(latlngs);
+
+ case 'Polygon':
+ latlngs = this.coordsToLatLngs(coords, 1);
+ return new L.Polygon(latlngs);
+
+ case 'MultiLineString':
+ latlngs = this.coordsToLatLngs(coords, 1);
+ return new L.MultiPolyline(latlngs);
+
+ case 'MultiPolygon':
+ latlngs = this.coordsToLatLngs(coords, 2);
+ return new L.MultiPolygon(latlngs);
+
+ case 'GeometryCollection':
+ for (i = 0, len = geometry.geometries.length; i < len; i++) {
+ layer = this.geometryToLayer({
+ geometry: geometry.geometries[i],
+ type: 'Feature',
+ properties: geojson.properties
+ }, pointToLayer);
+ layers.push(layer);
+ }
+ return new L.FeatureGroup(layers);
+
+ default:
+ throw new Error('Invalid GeoJSON object.');
+ }
+ },
+
+ coordsToLatLng: function (coords, reverse) { // (Array, Boolean) -> LatLng
+ var lat = parseFloat(coords[reverse ? 0 : 1]),
+ lng = parseFloat(coords[reverse ? 1 : 0]);
+
+ return new L.LatLng(lat, lng);
+ },
+
+ coordsToLatLngs: function (coords, levelsDeep, reverse) { // (Array, Number, Boolean) -> Array
+ var latlng,
+ latlngs = [],
+ i, len;
+
+ for (i = 0, len = coords.length; i < len; i++) {
+ latlng = levelsDeep ?
+ this.coordsToLatLngs(coords[i], levelsDeep - 1, reverse) :
+ this.coordsToLatLng(coords[i], reverse);
+
+ latlngs.push(latlng);
+ }
+
+ return latlngs;
+ }
+});
+
+L.geoJson = function (geojson, options) {
+ return new L.GeoJSON(geojson, options);
+};
+
+
+/*
+ * L.DomEvent contains functions for working with DOM events.
+ */
+
+L.DomEvent = {
+ /* inspired by John Resig, Dean Edwards and YUI addEvent implementations */
+ addListener: function (obj, type, fn, context) { // (HTMLElement, String, Function[, Object])
+
+ var id = L.stamp(fn),
+ key = '_leaflet_' + type + id,
+ handler, originalHandler, newType;
+
+ if (obj[key]) { return this; }
+
+ handler = function (e) {
+ return fn.call(context || obj, e || L.DomEvent._getEvent());
+ };
+
+ if (L.Browser.msTouch && type.indexOf('touch') === 0) {
+ return this.addMsTouchListener(obj, type, handler, id);
+ }
+ if (L.Browser.touch && (type === 'dblclick') && this.addDoubleTapListener) {
+ this.addDoubleTapListener(obj, handler, id);
+ }
+
+ if ('addEventListener' in obj) {
+
+ if (type === 'mousewheel') {
+ obj.addEventListener('DOMMouseScroll', handler, false);
+ obj.addEventListener(type, handler, false);
+
+ } else if ((type === 'mouseenter') || (type === 'mouseleave')) {
+
+ originalHandler = handler;
+ newType = (type === 'mouseenter' ? 'mouseover' : 'mouseout');
+
+ handler = function (e) {
+ if (!L.DomEvent._checkMouse(obj, e)) { return; }
+ return originalHandler(e);
+ };
+
+ obj.addEventListener(newType, handler, false);
+
+ } else {
+ obj.addEventListener(type, handler, false);
+ }
+
+ } else if ('attachEvent' in obj) {
+ obj.attachEvent("on" + type, handler);
+ }
+
+ obj[key] = handler;
+
+ return this;
+ },
+
+ removeListener: function (obj, type, fn) { // (HTMLElement, String, Function)
+
+ var id = L.stamp(fn),
+ key = '_leaflet_' + type + id,
+ handler = obj[key];
+
+ if (!handler) { return; }
+
+ if (L.Browser.msTouch && type.indexOf('touch') === 0) {
+ this.removeMsTouchListener(obj, type, id);
+ } else if (L.Browser.touch && (type === 'dblclick') && this.removeDoubleTapListener) {
+ this.removeDoubleTapListener(obj, id);
+
+ } else if ('removeEventListener' in obj) {
+
+ if (type === 'mousewheel') {
+ obj.removeEventListener('DOMMouseScroll', handler, false);
+ obj.removeEventListener(type, handler, false);
+
+ } else if ((type === 'mouseenter') || (type === 'mouseleave')) {
+ obj.removeEventListener((type === 'mouseenter' ? 'mouseover' : 'mouseout'), handler, false);
+ } else {
+ obj.removeEventListener(type, handler, false);
+ }
+ } else if ('detachEvent' in obj) {
+ obj.detachEvent("on" + type, handler);
+ }
+
+ obj[key] = null;
+
+ return this;
+ },
+
+ stopPropagation: function (e) {
+
+ if (e.stopPropagation) {
+ e.stopPropagation();
+ } else {
+ e.cancelBubble = true;
+ }
+ return this;
+ },
+
+ disableClickPropagation: function (el) {
+
+ var stop = L.DomEvent.stopPropagation;
+
+ for (var i = L.Draggable.START.length - 1; i >= 0; i--) {
+ L.DomEvent.addListener(el, L.Draggable.START[i], stop);
+ }
+
+ return L.DomEvent
+ .addListener(el, 'click', stop)
+ .addListener(el, 'dblclick', stop);
+ },
+
+ preventDefault: function (e) {
+
+ if (e.preventDefault) {
+ e.preventDefault();
+ } else {
+ e.returnValue = false;
+ }
+ return this;
+ },
+
+ stop: function (e) {
+ return L.DomEvent.preventDefault(e).stopPropagation(e);
+ },
+
+ getMousePosition: function (e, container) {
+
+ var body = document.body,
+ docEl = document.documentElement,
+ x = e.pageX ? e.pageX : e.clientX + body.scrollLeft + docEl.scrollLeft,
+ y = e.pageY ? e.pageY : e.clientY + body.scrollTop + docEl.scrollTop,
+ pos = new L.Point(x, y);
+
+ return (container ? pos._subtract(L.DomUtil.getViewportOffset(container)) : pos);
+ },
+
+ getWheelDelta: function (e) {
+
+ var delta = 0;
+
+ if (e.wheelDelta) {
+ delta = e.wheelDelta / 120;
+ }
+ if (e.detail) {
+ delta = -e.detail / 3;
+ }
+ return delta;
+ },
+
+ // check if element really left/entered the event target (for mouseenter/mouseleave)
+ _checkMouse: function (el, e) {
+
+ var related = e.relatedTarget;
+
+ if (!related) { return true; }
+
+ try {
+ while (related && (related !== el)) {
+ related = related.parentNode;
+ }
+ } catch (err) {
+ return false;
+ }
+ return (related !== el);
+ },
+
+ _getEvent: function () { // evil magic for IE
+ /*jshint noarg:false */
+ var e = window.event;
+ if (!e) {
+ var caller = arguments.callee.caller;
+ while (caller) {
+ e = caller['arguments'][0];
+ if (e && window.Event === e.constructor) {
+ break;
+ }
+ caller = caller.caller;
+ }
+ }
+ return e;
+ }
+};
+
+L.DomEvent.on = L.DomEvent.addListener;
+L.DomEvent.off = L.DomEvent.removeListener;
+
+
+/*
+ * L.Draggable allows you to add dragging capabilities to any element. Supports mobile devices too.
+ */
+
+L.Draggable = L.Class.extend({
+ includes: L.Mixin.Events,
+
+ statics: {
+ START: L.Browser.touch ? ['touchstart', 'mousedown'] : ['mousedown'],
+ END: {
+ mousedown: 'mouseup',
+ touchstart: 'touchend',
+ MSPointerDown: 'touchend'
+ },
+ MOVE: {
+ mousedown: 'mousemove',
+ touchstart: 'touchmove',
+ MSPointerDown: 'touchmove'
+ },
+ TAP_TOLERANCE: 15
+ },
+
+ initialize: function (element, dragStartTarget, longPress) {
+ this._element = element;
+ this._dragStartTarget = dragStartTarget || element;
+ this._longPress = longPress && !L.Browser.msTouch;
+ },
+
+ enable: function () {
+ if (this._enabled) { return; }
+
+ for (var i = L.Draggable.START.length - 1; i >= 0; i--) {
+ L.DomEvent.on(this._dragStartTarget, L.Draggable.START[i], this._onDown, this);
+ }
+ this._enabled = true;
+ },
+
+ disable: function () {
+ if (!this._enabled) { return; }
+
+ for (var i = L.Draggable.START.length - 1; i >= 0; i--) {
+ L.DomEvent.off(this._dragStartTarget, L.Draggable.START[i], this._onDown, this);
+ }
+ this._enabled = false;
+ this._moved = false;
+ },
+
+ _onDown: function (e) {
+ if ((!L.Browser.touch && e.shiftKey) ||
+ ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; }
+
+ L.DomEvent.preventDefault(e);
+ L.DomEvent.stopPropagation(e);
+
+ if (L.Draggable._disabled) { return; }
+
+ this._simulateClick = true;
+
+ if (e.touches && e.touches.length > 1) {
+ this._simulateClick = false;
+ clearTimeout(this._longPressTimeout);
+ return;
+ }
+
+ var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
+ el = first.target;
+
+ if (L.Browser.touch && el.tagName.toLowerCase() === 'a') {
+ L.DomUtil.addClass(el, 'leaflet-active');
+ }
+
+ this._moved = false;
+ if (this._moving) { return; }
+
+ this._startPoint = new L.Point(first.clientX, first.clientY);
+ this._startPos = this._newPos = L.DomUtil.getPosition(this._element);
+
+ //Touch contextmenu event emulation
+ if (e.touches && e.touches.length === 1 && L.Browser.touch && this._longPress) {
+ this._longPressTimeout = setTimeout(L.bind(function () {
+ var dist = (this._newPos && this._newPos.distanceTo(this._startPos)) || 0;
+
+ if (dist < L.Draggable.TAP_TOLERANCE) {
+ this._simulateClick = false;
+ this._onUp();
+ this._simulateEvent('contextmenu', first);
+ }
+ }, this), 1000);
+ }
+
+ L.DomEvent.on(document, L.Draggable.MOVE[e.type], this._onMove, this);
+ L.DomEvent.on(document, L.Draggable.END[e.type], this._onUp, this);
+ },
+
+ _onMove: function (e) {
+ if (e.touches && e.touches.length > 1) { return; }
+
+ var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
+ newPoint = new L.Point(first.clientX, first.clientY),
+ diffVec = newPoint.subtract(this._startPoint);
+
+ if (!diffVec.x && !diffVec.y) { return; }
+
+ L.DomEvent.preventDefault(e);
+
+ if (!this._moved) {
+ this.fire('dragstart');
+ this._moved = true;
+
+ this._startPos = L.DomUtil.getPosition(this._element).subtract(diffVec);
+
+ if (!L.Browser.touch) {
+ L.DomUtil.disableTextSelection();
+ this._setMovingCursor();
+ }
+ }
+
+ this._newPos = this._startPos.add(diffVec);
+ this._moving = true;
+
+ L.Util.cancelAnimFrame(this._animRequest);
+ this._animRequest = L.Util.requestAnimFrame(this._updatePosition, this, true, this._dragStartTarget);
+ },
+
+ _updatePosition: function () {
+ this.fire('predrag');
+ L.DomUtil.setPosition(this._element, this._newPos);
+ this.fire('drag');
+ },
+
+ _onUp: function (e) {
+ var simulateClickTouch;
+ clearTimeout(this._longPressTimeout);
+ if (this._simulateClick && e.changedTouches) {
+ var first = e.changedTouches[0],
+ el = first.target,
+ dist = (this._newPos && this._newPos.distanceTo(this._startPos)) || 0;
+
+ if (el.tagName.toLowerCase() === 'a') {
+ L.DomUtil.removeClass(el, 'leaflet-active');
+ }
+
+ if (dist < L.Draggable.TAP_TOLERANCE) {
+ simulateClickTouch = first;
+ }
+ }
+
+ if (!L.Browser.touch) {
+ L.DomUtil.enableTextSelection();
+ this._restoreCursor();
+ }
+
+ for (var i in L.Draggable.MOVE) {
+ if (L.Draggable.MOVE.hasOwnProperty(i)) {
+ L.DomEvent.off(document, L.Draggable.MOVE[i], this._onMove);
+ L.DomEvent.off(document, L.Draggable.END[i], this._onUp);
+ }
+ }
+
+ if (this._moved) {
+ // ensure drag is not fired after dragend
+ L.Util.cancelAnimFrame(this._animRequest);
+
+ this.fire('dragend');
+ }
+ this._moving = false;
+
+ if (simulateClickTouch) {
+ this._moved = false;
+ this._simulateEvent('click', simulateClickTouch);
+ }
+ },
+
+ _setMovingCursor: function () {
+ L.DomUtil.addClass(document.body, 'leaflet-dragging');
+ },
+
+ _restoreCursor: function () {
+ L.DomUtil.removeClass(document.body, 'leaflet-dragging');
+ },
+
+ _simulateEvent: function (type, e) {
+ var simulatedEvent = document.createEvent('MouseEvents');
+
+ simulatedEvent.initMouseEvent(
+ type, true, true, window, 1,
+ e.screenX, e.screenY,
+ e.clientX, e.clientY,
+ false, false, false, false, 0, null);
+
+ e.target.dispatchEvent(simulatedEvent);
+ }
+});
+
+
+/*
+ L.Handler is a base class for handler classes that are used internally to inject
+ interaction features like dragging to classes like Map and Marker.
+*/
+
+L.Handler = L.Class.extend({
+ initialize: function (map) {
+ this._map = map;
+ },
+
+ enable: function () {
+ if (this._enabled) { return; }
+
+ this._enabled = true;
+ this.addHooks();
+ },
+
+ disable: function () {
+ if (!this._enabled) { return; }
+
+ this._enabled = false;
+ this.removeHooks();
+ },
+
+ enabled: function () {
+ return !!this._enabled;
+ }
+});
+
+
+/*
+ * L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default.
+ */
+
+L.Map.mergeOptions({
+ dragging: true,
+
+ inertia: !L.Browser.android23,
+ inertiaDeceleration: 3400, // px/s^2
+ inertiaMaxSpeed: Infinity, // px/s
+ inertiaThreshold: L.Browser.touch ? 32 : 18, // ms
+ easeLinearity: 0.25,
+
+ longPress: true,
+
+ // TODO refactor, move to CRS
+ worldCopyJump: false
+});
+
+L.Map.Drag = L.Handler.extend({
+ addHooks: function () {
+ if (!this._draggable) {
+ var map = this._map;
+
+ this._draggable = new L.Draggable(map._mapPane, map._container, map.options.longPress);
+
+ this._draggable.on({
+ 'dragstart': this._onDragStart,
+ 'drag': this._onDrag,
+ 'dragend': this._onDragEnd
+ }, this);
+
+ if (map.options.worldCopyJump) {
+ this._draggable.on('predrag', this._onPreDrag, this);
+ map.on('viewreset', this._onViewReset, this);
+ }
+ }
+ this._draggable.enable();
+ },
+
+ removeHooks: function () {
+ this._draggable.disable();
+ },
+
+ moved: function () {
+ return this._draggable && this._draggable._moved;
+ },
+
+ _onDragStart: function () {
+ var map = this._map;
+
+ if (map._panAnim) {
+ map._panAnim.stop();
+ }
+
+ map
+ .fire('movestart')
+ .fire('dragstart');
+
+ if (map.options.inertia) {
+ this._positions = [];
+ this._times = [];
+ }
+ },
+
+ _onDrag: function () {
+ if (this._map.options.inertia) {
+ var time = this._lastTime = +new Date(),
+ pos = this._lastPos = this._draggable._newPos;
+
+ this._positions.push(pos);
+ this._times.push(time);
+
+ if (time - this._times[0] > 200) {
+ this._positions.shift();
+ this._times.shift();
+ }
+ }
+
+ this._map
+ .fire('move')
+ .fire('drag');
+ },
+
+ _onViewReset: function () {
+ // TODO fix hardcoded Earth values
+ var pxCenter = this._map.getSize()._divideBy(2),
+ pxWorldCenter = this._map.latLngToLayerPoint(new L.LatLng(0, 0));
+
+ this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x;
+ this._worldWidth = this._map.project(new L.LatLng(0, 180)).x;
+ },
+
+ _onPreDrag: function () {
+ // TODO refactor to be able to adjust map pane position after zoom
+ var worldWidth = this._worldWidth,
+ halfWidth = Math.round(worldWidth / 2),
+ dx = this._initialWorldOffset,
+ x = this._draggable._newPos.x,
+ newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx,
+ newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx,
+ newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2;
+
+ this._draggable._newPos.x = newX;
+ },
+
+ _onDragEnd: function () {
+ var map = this._map,
+ options = map.options,
+ delay = +new Date() - this._lastTime,
+
+ noInertia = !options.inertia || delay > options.inertiaThreshold || !this._positions[0];
+
+ if (noInertia) {
+ map.fire('moveend');
+
+ } else {
+
+ var direction = this._lastPos.subtract(this._positions[0]),
+ duration = (this._lastTime + delay - this._times[0]) / 1000,
+ ease = options.easeLinearity,
+
+ speedVector = direction.multiplyBy(ease / duration),
+ speed = speedVector.distanceTo(new L.Point(0, 0)),
+
+ limitedSpeed = Math.min(options.inertiaMaxSpeed, speed),
+ limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed),
+
+ decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease),
+ offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round();
+
+ L.Util.requestAnimFrame(function () {
+ map.panBy(offset, decelerationDuration, ease);
+ });
+ }
+
+ map.fire('dragend');
+
+ if (options.maxBounds) {
+ // TODO predrag validation instead of animation
+ L.Util.requestAnimFrame(this._panInsideMaxBounds, map, true, map._container);
+ }
+ },
+
+ _panInsideMaxBounds: function () {
+ this.panInsideBounds(this.options.maxBounds);
+ }
+});
+
+L.Map.addInitHook('addHandler', 'dragging', L.Map.Drag);
+
+
+/*
+ * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default.
+ */
+
+L.Map.mergeOptions({
+ doubleClickZoom: true
+});
+
+L.Map.DoubleClickZoom = L.Handler.extend({
+ addHooks: function () {
+ this._map.on('dblclick', this._onDoubleClick);
+ },
+
+ removeHooks: function () {
+ this._map.off('dblclick', this._onDoubleClick);
+ },
+
+ _onDoubleClick: function (e) {
+ this.setView(e.latlng, this._zoom + 1);
+ }
+});
+
+L.Map.addInitHook('addHandler', 'doubleClickZoom', L.Map.DoubleClickZoom);
+
+
+/*
+ * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map.
+ */
+
+L.Map.mergeOptions({
+ scrollWheelZoom: true
+});
+
+L.Map.ScrollWheelZoom = L.Handler.extend({
+ addHooks: function () {
+ L.DomEvent.on(this._map._container, 'mousewheel', this._onWheelScroll, this);
+ this._delta = 0;
+ },
+
+ removeHooks: function () {
+ L.DomEvent.off(this._map._container, 'mousewheel', this._onWheelScroll);
+ },
+
+ _onWheelScroll: function (e) {
+ var delta = L.DomEvent.getWheelDelta(e);
+
+ this._delta += delta;
+ this._lastMousePos = this._map.mouseEventToContainerPoint(e);
+
+ if (!this._startTime) {
+ this._startTime = +new Date();
+ }
+
+ var left = Math.max(40 - (+new Date() - this._startTime), 0);
+
+ clearTimeout(this._timer);
+ this._timer = setTimeout(L.bind(this._performZoom, this), left);
+
+ L.DomEvent.preventDefault(e);
+ L.DomEvent.stopPropagation(e);
+ },
+
+ _performZoom: function () {
+ var map = this._map,
+ delta = this._delta,
+ zoom = map.getZoom();
+
+ delta = delta > 0 ? Math.ceil(delta) : Math.round(delta);
+ delta = Math.max(Math.min(delta, 4), -4);
+ delta = map._limitZoom(zoom + delta) - zoom;
+
+ this._delta = 0;
+
+ this._startTime = null;
+
+ if (!delta) { return; }
+
+ var newZoom = zoom + delta,
+ newCenter = this._getCenterForScrollWheelZoom(newZoom);
+
+ map.setView(newCenter, newZoom);
+ },
+
+ _getCenterForScrollWheelZoom: function (newZoom) {
+ var map = this._map,
+ scale = map.getZoomScale(newZoom),
+ viewHalf = map.getSize()._divideBy(2),
+ centerOffset = this._lastMousePos._subtract(viewHalf)._multiplyBy(1 - 1 / scale),
+ newCenterPoint = map._getTopLeftPoint()._add(viewHalf)._add(centerOffset);
+
+ return map.unproject(newCenterPoint);
+ }
+});
+
+L.Map.addInitHook('addHandler', 'scrollWheelZoom', L.Map.ScrollWheelZoom);
+
+
+/*
+ * Extends the event handling code with double tap support for mobile browsers.
+ */
+
+L.extend(L.DomEvent, {
+
+ _touchstart: L.Browser.msTouch ? 'MSPointerDown' : 'touchstart',
+ _touchend: L.Browser.msTouch ? 'MSPointerUp' : 'touchend',
+
+ // inspired by Zepto touch code by Thomas Fuchs
+ addDoubleTapListener: function (obj, handler, id) {
+ var last,
+ doubleTap = false,
+ delay = 250,
+ touch,
+ pre = '_leaflet_',
+ touchstart = this._touchstart,
+ touchend = this._touchend,
+ trackedTouches = [];
+
+ function onTouchStart(e) {
+ var count;
+ if (L.Browser.msTouch) {
+ trackedTouches.push(e.pointerId);
+ count = trackedTouches.length;
+ } else {
+ count = e.touches.length;
+ }
+ if (count > 1) {
+ return;
+ }
+
+ var now = Date.now(),
+ delta = now - (last || now);
+
+ touch = e.touches ? e.touches[0] : e;
+ doubleTap = (delta > 0 && delta <= delay);
+ last = now;
+ }
+
+ function onTouchEnd(e) {
+ /*jshint forin:false */
+ if (L.Browser.msTouch) {
+ var idx = trackedTouches.indexOf(e.pointerId);
+ if (idx === -1) {
+ return;
+ }
+ trackedTouches.splice(idx, 1);
+ }
+
+ if (doubleTap) {
+ if (L.Browser.msTouch) {
+ //Work around .type being readonly with MSPointer* events
+ var newTouch = { },
+ prop;
+
+ for (var i in touch) {
+ prop = touch[i];
+ if (typeof prop === 'function') {
+ newTouch[i] = prop.bind(touch);
+ } else {
+ newTouch[i] = prop;
+ }
+ }
+ touch = newTouch;
+ }
+ touch.type = 'dblclick';
+ handler(touch);
+ last = null;
+ }
+ }
+ obj[pre + touchstart + id] = onTouchStart;
+ obj[pre + touchend + id] = onTouchEnd;
+
+ //On msTouch we need to listen on the document otherwise a drag starting on the map and moving off screen will not come through to us
+ // so we will lose track of how many touches are ongoing
+ var endElement = L.Browser.msTouch ? document.documentElement : obj;
+
+ obj.addEventListener(touchstart, onTouchStart, false);
+ endElement.addEventListener(touchend, onTouchEnd, false);
+ if (L.Browser.msTouch) {
+ endElement.addEventListener('MSPointerCancel', onTouchEnd, false);
+ }
+ return this;
+ },
+
+ removeDoubleTapListener: function (obj, id) {
+ var pre = '_leaflet_';
+ obj.removeEventListener(this._touchstart, obj[pre + this._touchstart + id], false);
+ (L.Browser.msTouch ? document.documentElement : obj).removeEventListener(this._touchend, obj[pre + this._touchend + id], false);
+ if (L.Browser.msTouch) {
+ document.documentElement.removeEventListener('MSPointerCancel', obj[pre + this._touchend + id], false);
+ }
+ return this;
+ }
+});
+
+
+/*
+ * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices.
+ */
+
+L.extend(L.DomEvent, {
+
+ _msTouches: [],
+ _msDocumentListener: false,
+
+ // Provides a touch events wrapper for msPointer events.
+ // Based on changes by veproza https://github.com/CloudMade/Leaflet/pull/1019
+
+ addMsTouchListener: function (obj, type, handler, id) {
+
+ switch (type) {
+ case 'touchstart':
+ return this.addMsTouchListenerStart(obj, type, handler, id);
+ case 'touchend':
+ return this.addMsTouchListenerEnd(obj, type, handler, id);
+ case 'touchmove':
+ return this.addMsTouchListenerMove(obj, type, handler, id);
+ default:
+ throw 'Unknown touch event type';
+ }
+ },
+
+ addMsTouchListenerStart: function (obj, type, handler, id) {
+ var pre = '_leaflet_',
+ touches = this._msTouches;
+
+ var cb = function (e) {
+
+ var alreadyInArray = false;
+ for (var i = 0; i < touches.length; i++) {
+ if (touches[i].pointerId === e.pointerId) {
+ alreadyInArray = true;
+ break;
+ }
+ }
+ if (!alreadyInArray) {
+ touches.push(e);
+ }
+
+ e.touches = touches.slice();
+ e.changedTouches = [e];
+
+ handler(e);
+ };
+
+ obj[pre + 'touchstart' + id] = cb;
+ obj.addEventListener('MSPointerDown', cb, false);
+
+ // need to also listen for end events to keep the _msTouches list accurate
+ // this needs to be on the body and never go away
+ if (!this._msDocumentListener) {
+ var internalCb = function (e) {
+ for (var i = 0; i < touches.length; i++) {
+ if (touches[i].pointerId === e.pointerId) {
+ touches.splice(i, 1);
+ break;
+ }
+ }
+ };
+ //We listen on the documentElement as any drags that end by moving the touch off the screen get fired there
+ document.documentElement.addEventListener('MSPointerUp', internalCb, false);
+ document.documentElement.addEventListener('MSPointerCancel', internalCb, false);
+
+ this._msDocumentListener = true;
+ }
+
+ return this;
+ },
+
+ addMsTouchListenerMove: function (obj, type, handler, id) {
+ var pre = '_leaflet_',
+ touches = this._msTouches;
+
+ function cb(e) {
+
+ // don't fire touch moves when mouse isn't down
+ if (e.pointerType === e.MSPOINTER_TYPE_MOUSE && e.buttons === 0) { return; }
+
+ for (var i = 0; i < touches.length; i++) {
+ if (touches[i].pointerId === e.pointerId) {
+ touches[i] = e;
+ break;
+ }
+ }
+
+ e.touches = touches.slice();
+ e.changedTouches = [e];
+
+ handler(e);
+ }
+
+ obj[pre + 'touchmove' + id] = cb;
+ obj.addEventListener('MSPointerMove', cb, false);
+
+ return this;
+ },
+
+ addMsTouchListenerEnd: function (obj, type, handler, id) {
+ var pre = '_leaflet_',
+ touches = this._msTouches;
+
+ var cb = function (e) {
+ for (var i = 0; i < touches.length; i++) {
+ if (touches[i].pointerId === e.pointerId) {
+ touches.splice(i, 1);
+ break;
+ }
+ }
+
+ e.touches = touches.slice();
+ e.changedTouches = [e];
+
+ handler(e);
+ };
+
+ obj[pre + 'touchend' + id] = cb;
+ obj.addEventListener('MSPointerUp', cb, false);
+ obj.addEventListener('MSPointerCancel', cb, false);
+
+ return this;
+ },
+
+ removeMsTouchListener: function (obj, type, id) {
+ var pre = '_leaflet_',
+ cb = obj[pre + type + id];
+
+ switch (type) {
+ case 'touchstart':
+ obj.removeEventListener('MSPointerDown', cb, false);
+ break;
+ case 'touchmove':
+ obj.removeEventListener('MSPointerMove', cb, false);
+ break;
+ case 'touchend':
+ obj.removeEventListener('MSPointerUp', cb, false);
+ obj.removeEventListener('MSPointerCancel', cb, false);
+ break;
+ }
+
+ return this;
+ }
+});
+
+
+/*
+ * L.Handler.TouchZoom is used by L.Map to add pinch zoom on supported mobile browsers.
+ */
+
+L.Map.mergeOptions({
+ touchZoom: L.Browser.touch && !L.Browser.android23
+});
+
+L.Map.TouchZoom = L.Handler.extend({
+ addHooks: function () {
+ L.DomEvent.on(this._map._container, 'touchstart', this._onTouchStart, this);
+ },
+
+ removeHooks: function () {
+ L.DomEvent.off(this._map._container, 'touchstart', this._onTouchStart, this);
+ },
+
+ _onTouchStart: function (e) {
+ var map = this._map;
+
+ if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; }
+
+ var p1 = map.mouseEventToLayerPoint(e.touches[0]),
+ p2 = map.mouseEventToLayerPoint(e.touches[1]),
+ viewCenter = map._getCenterLayerPoint();
+
+ this._startCenter = p1.add(p2)._divideBy(2);
+ this._startDist = p1.distanceTo(p2);
+
+ this._moved = false;
+ this._zooming = true;
+
+ this._centerOffset = viewCenter.subtract(this._startCenter);
+
+ if (map._panAnim) {
+ map._panAnim.stop();
+ }
+
+ L.DomEvent
+ .on(document, 'touchmove', this._onTouchMove, this)
+ .on(document, 'touchend', this._onTouchEnd, this);
+
+ L.DomEvent.preventDefault(e);
+ },
+
+ _onTouchMove: function (e) {
+ if (!e.touches || e.touches.length !== 2) { return; }
+
+ var map = this._map;
+
+ var p1 = map.mouseEventToLayerPoint(e.touches[0]),
+ p2 = map.mouseEventToLayerPoint(e.touches[1]);
+
+ this._scale = p1.distanceTo(p2) / this._startDist;
+ this._delta = p1._add(p2)._divideBy(2)._subtract(this._startCenter);
+
+ if (this._scale === 1) { return; }
+
+ if (!this._moved) {
+ L.DomUtil.addClass(map._mapPane, 'leaflet-zoom-anim leaflet-touching');
+
+ map
+ .fire('movestart')
+ .fire('zoomstart')
+ ._prepareTileBg();
+
+ this._moved = true;
+ }
+
+ L.Util.cancelAnimFrame(this._animRequest);
+ this._animRequest = L.Util.requestAnimFrame(
+ this._updateOnMove, this, true, this._map._container);
+
+ L.DomEvent.preventDefault(e);
+ },
+
+ _updateOnMove: function () {
+ var map = this._map,
+ origin = this._getScaleOrigin(),
+ center = map.layerPointToLatLng(origin);
+
+ map.fire('zoomanim', {
+ center: center,
+ zoom: map.getScaleZoom(this._scale)
+ });
+
+ // Used 2 translates instead of transform-origin because of a very strange bug -
+ // it didn't count the origin on the first touch-zoom but worked correctly afterwards
+
+ map._tileBg.style[L.DomUtil.TRANSFORM] =
+ L.DomUtil.getTranslateString(this._delta) + ' ' +
+ L.DomUtil.getScaleString(this._scale, this._startCenter);
+ },
+
+ _onTouchEnd: function () {
+ if (!this._moved || !this._zooming) { return; }
+
+ var map = this._map;
+
+ this._zooming = false;
+ L.DomUtil.removeClass(map._mapPane, 'leaflet-touching');
+
+ L.DomEvent
+ .off(document, 'touchmove', this._onTouchMove)
+ .off(document, 'touchend', this._onTouchEnd);
+
+ var origin = this._getScaleOrigin(),
+ center = map.layerPointToLatLng(origin),
+
+ oldZoom = map.getZoom(),
+ floatZoomDelta = map.getScaleZoom(this._scale) - oldZoom,
+ roundZoomDelta = (floatZoomDelta > 0 ?
+ Math.ceil(floatZoomDelta) : Math.floor(floatZoomDelta)),
+
+ zoom = map._limitZoom(oldZoom + roundZoomDelta);
+
+ map.fire('zoomanim', {
+ center: center,
+ zoom: zoom
+ });
+
+ map._runAnimation(center, zoom, map.getZoomScale(zoom) / this._scale, origin, true);
+ },
+
+ _getScaleOrigin: function () {
+ var centerOffset = this._centerOffset.subtract(this._delta).divideBy(this._scale);
+ return this._startCenter.add(centerOffset);
+ }
+});
+
+L.Map.addInitHook('addHandler', 'touchZoom', L.Map.TouchZoom);
+
+
+/*
+ * L.Handler.ShiftDragZoom is used to add shift-drag zoom interaction to the map
+ * (zoom to a selected bounding box), enabled by default.
+ */
+
+L.Map.mergeOptions({
+ boxZoom: true
+});
+
+L.Map.BoxZoom = L.Handler.extend({
+ initialize: function (map) {
+ this._map = map;
+ this._container = map._container;
+ this._pane = map._panes.overlayPane;
+ },
+
+ addHooks: function () {
+ L.DomEvent.on(this._container, 'mousedown', this._onMouseDown, this);
+ },
+
+ removeHooks: function () {
+ L.DomEvent.off(this._container, 'mousedown', this._onMouseDown);
+ },
+
+ _onMouseDown: function (e) {
+ if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; }
+
+ L.DomUtil.disableTextSelection();
+
+ this._startLayerPoint = this._map.mouseEventToLayerPoint(e);
+
+ this._box = L.DomUtil.create('div', 'leaflet-zoom-box', this._pane);
+ L.DomUtil.setPosition(this._box, this._startLayerPoint);
+
+ //TODO refactor: move cursor to styles
+ this._container.style.cursor = 'crosshair';
+
+ L.DomEvent
+ .on(document, 'mousemove', this._onMouseMove, this)
+ .on(document, 'mouseup', this._onMouseUp, this)
+ .preventDefault(e);
+
+ this._map.fire("boxzoomstart");
+ },
+
+ _onMouseMove: function (e) {
+ var startPoint = this._startLayerPoint,
+ box = this._box,
+
+ layerPoint = this._map.mouseEventToLayerPoint(e),
+ offset = layerPoint.subtract(startPoint),
+
+ newPos = new L.Point(
+ Math.min(layerPoint.x, startPoint.x),
+ Math.min(layerPoint.y, startPoint.y));
+
+ L.DomUtil.setPosition(box, newPos);
+
+ // TODO refactor: remove hardcoded 4 pixels
+ box.style.width = (Math.max(0, Math.abs(offset.x) - 4)) + 'px';
+ box.style.height = (Math.max(0, Math.abs(offset.y) - 4)) + 'px';
+ },
+
+ _onMouseUp: function (e) {
+ this._pane.removeChild(this._box);
+ this._container.style.cursor = '';
+
+ L.DomUtil.enableTextSelection();
+
+ L.DomEvent
+ .off(document, 'mousemove', this._onMouseMove)
+ .off(document, 'mouseup', this._onMouseUp);
+
+ var map = this._map,
+ layerPoint = map.mouseEventToLayerPoint(e);
+
+ if (this._startLayerPoint.equals(layerPoint)) { return; }
+
+ var bounds = new L.LatLngBounds(
+ map.layerPointToLatLng(this._startLayerPoint),
+ map.layerPointToLatLng(layerPoint));
+
+ map.fitBounds(bounds);
+
+ map.fire("boxzoomend", {
+ boxZoomBounds: bounds
+ });
+ }
+});
+
+L.Map.addInitHook('addHandler', 'boxZoom', L.Map.BoxZoom);
+
+
+/*
+ * L.Map.Keyboard is handling keyboard interaction with the map, enabled by default.
+ */
+
+L.Map.mergeOptions({
+ keyboard: true,
+ keyboardPanOffset: 80,
+ keyboardZoomOffset: 1
+});
+
+L.Map.Keyboard = L.Handler.extend({
+
+ keyCodes: {
+ left: [37],
+ right: [39],
+ down: [40],
+ up: [38],
+ zoomIn: [187, 107, 61],
+ zoomOut: [189, 109, 173]
+ },
+
+ initialize: function (map) {
+ this._map = map;
+
+ this._setPanOffset(map.options.keyboardPanOffset);
+ this._setZoomOffset(map.options.keyboardZoomOffset);
+ },
+
+ addHooks: function () {
+ var container = this._map._container;
+
+ // make the container focusable by tabbing
+ if (container.tabIndex === -1) {
+ container.tabIndex = "0";
+ }
+
+ L.DomEvent
+ .on(container, 'focus', this._onFocus, this)
+ .on(container, 'blur', this._onBlur, this)
+ .on(container, 'mousedown', this._onMouseDown, this);
+
+ this._map
+ .on('focus', this._addHooks, this)
+ .on('blur', this._removeHooks, this);
+ },
+
+ removeHooks: function () {
+ this._removeHooks();
+
+ var container = this._map._container;
+
+ L.DomEvent
+ .off(container, 'focus', this._onFocus, this)
+ .off(container, 'blur', this._onBlur, this)
+ .off(container, 'mousedown', this._onMouseDown, this);
+
+ this._map
+ .off('focus', this._addHooks, this)
+ .off('blur', this._removeHooks, this);
+ },
+
+ _onMouseDown: function () {
+ if (!this._focused) {
+ this._map._container.focus();
+ }
+ },
+
+ _onFocus: function () {
+ this._focused = true;
+ this._map.fire('focus');
+ },
+
+ _onBlur: function () {
+ this._focused = false;
+ this._map.fire('blur');
+ },
+
+ _setPanOffset: function (pan) {
+ var keys = this._panKeys = {},
+ codes = this.keyCodes,
+ i, len;
+
+ for (i = 0, len = codes.left.length; i < len; i++) {
+ keys[codes.left[i]] = [-1 * pan, 0];
+ }
+ for (i = 0, len = codes.right.length; i < len; i++) {
+ keys[codes.right[i]] = [pan, 0];
+ }
+ for (i = 0, len = codes.down.length; i < len; i++) {
+ keys[codes.down[i]] = [0, pan];
+ }
+ for (i = 0, len = codes.up.length; i < len; i++) {
+ keys[codes.up[i]] = [0, -1 * pan];
+ }
+ },
+
+ _setZoomOffset: function (zoom) {
+ var keys = this._zoomKeys = {},
+ codes = this.keyCodes,
+ i, len;
+
+ for (i = 0, len = codes.zoomIn.length; i < len; i++) {
+ keys[codes.zoomIn[i]] = zoom;
+ }
+ for (i = 0, len = codes.zoomOut.length; i < len; i++) {
+ keys[codes.zoomOut[i]] = -zoom;
+ }
+ },
+
+ _addHooks: function () {
+ L.DomEvent.on(document, 'keydown', this._onKeyDown, this);
+ },
+
+ _removeHooks: function () {
+ L.DomEvent.off(document, 'keydown', this._onKeyDown, this);
+ },
+
+ _onKeyDown: function (e) {
+ var key = e.keyCode,
+ map = this._map;
+
+ if (this._panKeys.hasOwnProperty(key)) {
+ map.panBy(this._panKeys[key]);
+
+ if (map.options.maxBounds) {
+ map.panInsideBounds(map.options.maxBounds);
+ }
+
+ } else if (this._zoomKeys.hasOwnProperty(key)) {
+ map.setZoom(map.getZoom() + this._zoomKeys[key]);
+
+ } else {
+ return;
+ }
+
+ L.DomEvent.stop(e);
+ }
+});
+
+L.Map.addInitHook('addHandler', 'keyboard', L.Map.Keyboard);
+
+
+/*
+ * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable.
+ */
+
+L.Handler.MarkerDrag = L.Handler.extend({
+ initialize: function (marker) {
+ this._marker = marker;
+ },
+
+ addHooks: function () {
+ var icon = this._marker._icon;
+ if (!this._draggable) {
+ this._draggable = new L.Draggable(icon, icon)
+ .on('dragstart', this._onDragStart, this)
+ .on('drag', this._onDrag, this)
+ .on('dragend', this._onDragEnd, this);
+ }
+ this._draggable.enable();
+ },
+
+ removeHooks: function () {
+ this._draggable.disable();
+ },
+
+ moved: function () {
+ return this._draggable && this._draggable._moved;
+ },
+
+ _onDragStart: function () {
+ this._marker
+ .closePopup()
+ .fire('movestart')
+ .fire('dragstart');
+ },
+
+ _onDrag: function () {
+ var marker = this._marker,
+ shadow = marker._shadow,
+ iconPos = L.DomUtil.getPosition(marker._icon),
+ latlng = marker._map.layerPointToLatLng(iconPos);
+
+ // update shadow position
+ if (shadow) {
+ L.DomUtil.setPosition(shadow, iconPos);
+ }
+
+ marker._latlng = latlng;
+
+ marker
+ .fire('move', {latlng: latlng})
+ .fire('drag');
+ },
+
+ _onDragEnd: function () {
+ this._marker
+ .fire('moveend')
+ .fire('dragend');
+ }
+});
+
+
+/*
+ * L.Handler.PolyEdit is an editing handler for polylines and polygons.
+ */
+
+L.Handler.PolyEdit = L.Handler.extend({
+ options: {
+ icon: new L.DivIcon({
+ iconSize: new L.Point(8, 8),
+ className: 'leaflet-div-icon leaflet-editing-icon'
+ })
+ },
+
+ initialize: function (poly, options) {
+ this._poly = poly;
+ L.setOptions(this, options);
+ },
+
+ addHooks: function () {
+ if (this._poly._map) {
+ if (!this._markerGroup) {
+ this._initMarkers();
+ }
+ this._poly._map.addLayer(this._markerGroup);
+ }
+ },
+
+ removeHooks: function () {
+ if (this._poly._map) {
+ this._poly._map.removeLayer(this._markerGroup);
+ delete this._markerGroup;
+ delete this._markers;
+ }
+ },
+
+ updateMarkers: function () {
+ this._markerGroup.clearLayers();
+ this._initMarkers();
+ },
+
+ _initMarkers: function () {
+ if (!this._markerGroup) {
+ this._markerGroup = new L.LayerGroup();
+ }
+ this._markers = [];
+
+ var latlngs = this._poly._latlngs,
+ i, j, len, marker;
+
+ // TODO refactor holes implementation in Polygon to support it here
+
+ for (i = 0, len = latlngs.length; i < len; i++) {
+
+ marker = this._createMarker(latlngs[i], i);
+ marker.on('click', this._onMarkerClick, this);
+ this._markers.push(marker);
+ }
+
+ var markerLeft, markerRight;
+
+ for (i = 0, j = len - 1; i < len; j = i++) {
+ if (i === 0 && !(L.Polygon && (this._poly instanceof L.Polygon))) {
+ continue;
+ }
+
+ markerLeft = this._markers[j];
+ markerRight = this._markers[i];
+
+ this._createMiddleMarker(markerLeft, markerRight);
+ this._updatePrevNext(markerLeft, markerRight);
+ }
+ },
+
+ _createMarker: function (latlng, index) {
+ var marker = new L.Marker(latlng, {
+ draggable: true,
+ icon: this.options.icon
+ });
+
+ marker._origLatLng = latlng;
+ marker._index = index;
+
+ marker.on('drag', this._onMarkerDrag, this);
+ marker.on('dragend', this._fireEdit, this);
+
+ this._markerGroup.addLayer(marker);
+
+ return marker;
+ },
+
+ _fireEdit: function () {
+ this._poly.fire('edit');
+ },
+
+ _onMarkerDrag: function (e) {
+ var marker = e.target;
+
+ L.extend(marker._origLatLng, marker._latlng);
+
+ if (marker._middleLeft) {
+ marker._middleLeft.setLatLng(this._getMiddleLatLng(marker._prev, marker));
+ }
+ if (marker._middleRight) {
+ marker._middleRight.setLatLng(this._getMiddleLatLng(marker, marker._next));
+ }
+
+ this._poly.redraw();
+ },
+
+ _onMarkerClick: function (e) {
+ // we want to remove the marker on click, but if latlng count < 3, polyline would be invalid
+ if (this._poly._latlngs.length < 3) { return; }
+
+ var marker = e.target,
+ i = marker._index;
+
+ // remove the marker
+ this._markerGroup.removeLayer(marker);
+ this._markers.splice(i, 1);
+ this._poly.spliceLatLngs(i, 1);
+ this._updateIndexes(i, -1);
+
+ // update prev/next links of adjacent markers
+ this._updatePrevNext(marker._prev, marker._next);
+
+ // remove ghost markers near the removed marker
+ if (marker._middleLeft) {
+ this._markerGroup.removeLayer(marker._middleLeft);
+ }
+ if (marker._middleRight) {
+ this._markerGroup.removeLayer(marker._middleRight);
+ }
+
+ // create a ghost marker in place of the removed one
+ if (marker._prev && marker._next) {
+ this._createMiddleMarker(marker._prev, marker._next);
+
+ } else if (!marker._prev) {
+ marker._next._middleLeft = null;
+
+ } else if (!marker._next) {
+ marker._prev._middleRight = null;
+ }
+
+ this._poly.fire('edit');
+ },
+
+ _updateIndexes: function (index, delta) {
+ this._markerGroup.eachLayer(function (marker) {
+ if (marker._index > index) {
+ marker._index += delta;
+ }
+ });
+ },
+
+ _createMiddleMarker: function (marker1, marker2) {
+ var latlng = this._getMiddleLatLng(marker1, marker2),
+ marker = this._createMarker(latlng),
+ onClick,
+ onDragStart,
+ onDragEnd;
+
+ marker.setOpacity(0.6);
+
+ marker1._middleRight = marker2._middleLeft = marker;
+
+ onDragStart = function () {
+ var i = marker2._index;
+
+ marker._index = i;
+
+ marker
+ .off('click', onClick)
+ .on('click', this._onMarkerClick, this);
+
+ latlng.lat = marker.getLatLng().lat;
+ latlng.lng = marker.getLatLng().lng;
+ this._poly.spliceLatLngs(i, 0, latlng);
+ this._markers.splice(i, 0, marker);
+
+ marker.setOpacity(1);
+
+ this._updateIndexes(i, 1);
+ marker2._index++;
+ this._updatePrevNext(marker1, marker);
+ this._updatePrevNext(marker, marker2);
+ };
+
+ onDragEnd = function () {
+ marker.off('dragstart', onDragStart, this);
+ marker.off('dragend', onDragEnd, this);
+
+ this._createMiddleMarker(marker1, marker);
+ this._createMiddleMarker(marker, marker2);
+ };
+
+ onClick = function () {
+ onDragStart.call(this);
+ onDragEnd.call(this);
+ this._poly.fire('edit');
+ };
+
+ marker
+ .on('click', onClick, this)
+ .on('dragstart', onDragStart, this)
+ .on('dragend', onDragEnd, this);
+
+ this._markerGroup.addLayer(marker);
+ },
+
+ _updatePrevNext: function (marker1, marker2) {
+ if (marker1) {
+ marker1._next = marker2;
+ }
+ if (marker2) {
+ marker2._prev = marker1;
+ }
+ },
+
+ _getMiddleLatLng: function (marker1, marker2) {
+ var map = this._poly._map,
+ p1 = map.latLngToLayerPoint(marker1.getLatLng()),
+ p2 = map.latLngToLayerPoint(marker2.getLatLng());
+
+ return map.layerPointToLatLng(p1._add(p2)._divideBy(2));
+ }
+});
+
+L.Polyline.addInitHook(function () {
+
+ if (L.Handler.PolyEdit) {
+ this.editing = new L.Handler.PolyEdit(this);
+
+ if (this.options.editable) {
+ this.editing.enable();
+ }
+ }
+
+ this.on('add', function () {
+ if (this.editing && this.editing.enabled()) {
+ this.editing.addHooks();
+ }
+ });
+
+ this.on('remove', function () {
+ if (this.editing && this.editing.enabled()) {
+ this.editing.removeHooks();
+ }
+ });
+});
+
+
+/*
+ * L.Control is a base class for implementing map controls. Handles positioning.
+ * All other controls extend from this class.
+ */
+
+L.Control = L.Class.extend({
+ options: {
+ position: 'topright'
+ },
+
+ initialize: function (options) {
+ L.setOptions(this, options);
+ },
+
+ getPosition: function () {
+ return this.options.position;
+ },
+
+ setPosition: function (position) {
+ var map = this._map;
+
+ if (map) {
+ map.removeControl(this);
+ }
+
+ this.options.position = position;
+
+ if (map) {
+ map.addControl(this);
+ }
+
+ return this;
+ },
+
+ addTo: function (map) {
+ this._map = map;
+
+ var container = this._container = this.onAdd(map),
+ pos = this.getPosition(),
+ corner = map._controlCorners[pos];
+
+ L.DomUtil.addClass(container, 'leaflet-control');
+
+ if (pos.indexOf('bottom') !== -1) {
+ corner.insertBefore(container, corner.firstChild);
+ } else {
+ corner.appendChild(container);
+ }
+
+ return this;
+ },
+
+ removeFrom: function (map) {
+ var pos = this.getPosition(),
+ corner = map._controlCorners[pos];
+
+ corner.removeChild(this._container);
+ this._map = null;
+
+ if (this.onRemove) {
+ this.onRemove(map);
+ }
+
+ return this;
+ }
+});
+
+L.control = function (options) {
+ return new L.Control(options);
+};
+
+
+/*
+ * Adds control-related methods to L.Map.
+ */
+
+L.Map.include({
+ addControl: function (control) {
+ control.addTo(this);
+ return this;
+ },
+
+ removeControl: function (control) {
+ control.removeFrom(this);
+ return this;
+ },
+
+ _initControlPos: function () {
+ var corners = this._controlCorners = {},
+ l = 'leaflet-',
+ container = this._controlContainer =
+ L.DomUtil.create('div', l + 'control-container', this._container);
+
+ function createCorner(vSide, hSide) {
+ var className = l + vSide + ' ' + l + hSide;
+
+ corners[vSide + hSide] = L.DomUtil.create('div', className, container);
+ }
+
+ createCorner('top', 'left');
+ createCorner('top', 'right');
+ createCorner('bottom', 'left');
+ createCorner('bottom', 'right');
+ }
+});
+
+
+/*
+ * L.Control.Zoom is used for the default zoom buttons on the map.
+ */
+
+L.Control.Zoom = L.Control.extend({
+ options: {
+ position: 'topleft'
+ },
+
+ onAdd: function (map) {
+ var zoomName = 'leaflet-control-zoom',
+ barName = 'leaflet-bar',
+ partName = barName + '-part',
+ container = L.DomUtil.create('div', zoomName + ' ' + barName);
+
+ this._map = map;
+
+ this._zoomInButton = this._createButton('+', 'Zoom in',
+ zoomName + '-in ' +
+ partName + ' ' +
+ partName + '-top',
+ container, this._zoomIn, this);
+
+ this._zoomOutButton = this._createButton('-', 'Zoom out',
+ zoomName + '-out ' +
+ partName + ' ' +
+ partName + '-bottom',
+ container, this._zoomOut, this);
+
+ map.on('zoomend', this._updateDisabled, this);
+
+ return container;
+ },
+
+ onRemove: function (map) {
+ map.off('zoomend', this._updateDisabled, this);
+ },
+
+ _zoomIn: function (e) {
+ this._map.zoomIn(e.shiftKey ? 3 : 1);
+ },
+
+ _zoomOut: function (e) {
+ this._map.zoomOut(e.shiftKey ? 3 : 1);
+ },
+
+ _createButton: function (html, title, className, container, fn, context) {
+ var link = L.DomUtil.create('a', className, container);
+ link.innerHTML = html;
+ link.href = '#';
+ link.title = title;
+
+ var stop = L.DomEvent.stopPropagation;
+
+ L.DomEvent
+ .on(link, 'click', stop)
+ .on(link, 'mousedown', stop)
+ .on(link, 'dblclick', stop)
+ .on(link, 'click', L.DomEvent.preventDefault)
+ .on(link, 'click', fn, context);
+
+ return link;
+ },
+
+ _updateDisabled: function () {
+ var map = this._map,
+ className = 'leaflet-control-zoom-disabled';
+
+ L.DomUtil.removeClass(this._zoomInButton, className);
+ L.DomUtil.removeClass(this._zoomOutButton, className);
+
+ if (map._zoom === map.getMinZoom()) {
+ L.DomUtil.addClass(this._zoomOutButton, className);
+ }
+ if (map._zoom === map.getMaxZoom()) {
+ L.DomUtil.addClass(this._zoomInButton, className);
+ }
+ }
+});
+
+L.Map.mergeOptions({
+ zoomControl: true
+});
+
+L.Map.addInitHook(function () {
+ if (this.options.zoomControl) {
+ this.zoomControl = new L.Control.Zoom();
+ this.addControl(this.zoomControl);
+ }
+});
+
+L.control.zoom = function (options) {
+ return new L.Control.Zoom(options);
+};
+
+
+
+/*
+ * L.Control.Attribution is used for displaying attribution on the map (added by default).
+ */
+
+L.Control.Attribution = L.Control.extend({
+ options: {
+ position: 'bottomright',
+ prefix: 'Powered by Leaflet '
+ },
+
+ initialize: function (options) {
+ L.setOptions(this, options);
+
+ this._attributions = {};
+ },
+
+ onAdd: function (map) {
+ this._container = L.DomUtil.create('div', 'leaflet-control-attribution');
+ L.DomEvent.disableClickPropagation(this._container);
+
+ map
+ .on('layeradd', this._onLayerAdd, this)
+ .on('layerremove', this._onLayerRemove, this);
+
+ this._update();
+
+ return this._container;
+ },
+
+ onRemove: function (map) {
+ map
+ .off('layeradd', this._onLayerAdd)
+ .off('layerremove', this._onLayerRemove);
+
+ },
+
+ setPrefix: function (prefix) {
+ this.options.prefix = prefix;
+ this._update();
+ return this;
+ },
+
+ addAttribution: function (text) {
+ if (!text) { return; }
+
+ if (!this._attributions[text]) {
+ this._attributions[text] = 0;
+ }
+ this._attributions[text]++;
+
+ this._update();
+
+ return this;
+ },
+
+ removeAttribution: function (text) {
+ if (!text) { return; }
+
+ this._attributions[text]--;
+ this._update();
+
+ return this;
+ },
+
+ _update: function () {
+ if (!this._map) { return; }
+
+ var attribs = [];
+
+ for (var i in this._attributions) {
+ if (this._attributions.hasOwnProperty(i) && this._attributions[i]) {
+ attribs.push(i);
+ }
+ }
+
+ var prefixAndAttribs = [];
+
+ if (this.options.prefix) {
+ prefixAndAttribs.push(this.options.prefix);
+ }
+ if (attribs.length) {
+ prefixAndAttribs.push(attribs.join(', '));
+ }
+
+ this._container.innerHTML = prefixAndAttribs.join(' — ');
+ },
+
+ _onLayerAdd: function (e) {
+ if (e.layer.getAttribution) {
+ this.addAttribution(e.layer.getAttribution());
+ }
+ },
+
+ _onLayerRemove: function (e) {
+ if (e.layer.getAttribution) {
+ this.removeAttribution(e.layer.getAttribution());
+ }
+ }
+});
+
+L.Map.mergeOptions({
+ attributionControl: true
+});
+
+L.Map.addInitHook(function () {
+ if (this.options.attributionControl) {
+ this.attributionControl = (new L.Control.Attribution()).addTo(this);
+ }
+});
+
+L.control.attribution = function (options) {
+ return new L.Control.Attribution(options);
+};
+
+
+/*
+ * L.Control.Scale is used for displaying metric/imperial scale on the map.
+ */
+
+L.Control.Scale = L.Control.extend({
+ options: {
+ position: 'bottomleft',
+ maxWidth: 100,
+ metric: true,
+ imperial: true,
+ updateWhenIdle: false
+ },
+
+ onAdd: function (map) {
+ this._map = map;
+
+ var className = 'leaflet-control-scale',
+ container = L.DomUtil.create('div', className),
+ options = this.options;
+
+ this._addScales(options, className, container);
+
+ map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
+ map.whenReady(this._update, this);
+
+ return container;
+ },
+
+ onRemove: function (map) {
+ map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
+ },
+
+ _addScales: function (options, className, container) {
+ if (options.metric) {
+ this._mScale = L.DomUtil.create('div', className + '-line', container);
+ }
+ if (options.imperial) {
+ this._iScale = L.DomUtil.create('div', className + '-line', container);
+ }
+ },
+
+ _update: function () {
+ var bounds = this._map.getBounds(),
+ centerLat = bounds.getCenter().lat,
+ halfWorldMeters = 6378137 * Math.PI * Math.cos(centerLat * Math.PI / 180),
+ dist = halfWorldMeters * (bounds.getNorthEast().lng - bounds.getSouthWest().lng) / 180,
+
+ size = this._map.getSize(),
+ options = this.options,
+ maxMeters = 0;
+
+ if (size.x > 0) {
+ maxMeters = dist * (options.maxWidth / size.x);
+ }
+
+ this._updateScales(options, maxMeters);
+ },
+
+ _updateScales: function (options, maxMeters) {
+ if (options.metric && maxMeters) {
+ this._updateMetric(maxMeters);
+ }
+
+ if (options.imperial && maxMeters) {
+ this._updateImperial(maxMeters);
+ }
+ },
+
+ _updateMetric: function (maxMeters) {
+ var meters = this._getRoundNum(maxMeters);
+
+ this._mScale.style.width = this._getScaleWidth(meters / maxMeters) + 'px';
+ this._mScale.innerHTML = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km';
+ },
+
+ _updateImperial: function (maxMeters) {
+ var maxFeet = maxMeters * 3.2808399,
+ scale = this._iScale,
+ maxMiles, miles, feet;
+
+ if (maxFeet > 5280) {
+ maxMiles = maxFeet / 5280;
+ miles = this._getRoundNum(maxMiles);
+
+ scale.style.width = this._getScaleWidth(miles / maxMiles) + 'px';
+ scale.innerHTML = miles + ' mi';
+
+ } else {
+ feet = this._getRoundNum(maxFeet);
+
+ scale.style.width = this._getScaleWidth(feet / maxFeet) + 'px';
+ scale.innerHTML = feet + ' ft';
+ }
+ },
+
+ _getScaleWidth: function (ratio) {
+ return Math.round(this.options.maxWidth * ratio) - 10;
+ },
+
+ _getRoundNum: function (num) {
+ var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1),
+ d = num / pow10;
+
+ d = d >= 10 ? 10 : d >= 5 ? 5 : d >= 3 ? 3 : d >= 2 ? 2 : 1;
+
+ return pow10 * d;
+ }
+});
+
+L.control.scale = function (options) {
+ return new L.Control.Scale(options);
+};
+
+
+/*
+ * L.Control.Layers is a control to allow users to switch between different layers on the map.
+ */
+
+L.Control.Layers = L.Control.extend({
+ options: {
+ collapsed: true,
+ position: 'topright',
+ autoZIndex: true
+ },
+
+ initialize: function (baseLayers, overlays, options) {
+ L.setOptions(this, options);
+
+ this._layers = {};
+ this._lastZIndex = 0;
+ this._handlingClick = false;
+
+ for (var i in baseLayers) {
+ if (baseLayers.hasOwnProperty(i)) {
+ this._addLayer(baseLayers[i], i);
+ }
+ }
+
+ for (i in overlays) {
+ if (overlays.hasOwnProperty(i)) {
+ this._addLayer(overlays[i], i, true);
+ }
+ }
+ },
+
+ onAdd: function (map) {
+ this._initLayout();
+ this._update();
+
+ map
+ .on('layeradd', this._onLayerChange, this)
+ .on('layerremove', this._onLayerChange, this);
+
+ return this._container;
+ },
+
+ onRemove: function (map) {
+ map
+ .off('layeradd', this._onLayerChange)
+ .off('layerremove', this._onLayerChange);
+ },
+
+ addBaseLayer: function (layer, name) {
+ this._addLayer(layer, name);
+ this._update();
+ return this;
+ },
+
+ addOverlay: function (layer, name) {
+ this._addLayer(layer, name, true);
+ this._update();
+ return this;
+ },
+
+ removeLayer: function (layer) {
+ var id = L.stamp(layer);
+ delete this._layers[id];
+ this._update();
+ return this;
+ },
+
+ _initLayout: function () {
+ var className = 'leaflet-control-layers',
+ container = this._container = L.DomUtil.create('div', className);
+
+ if (!L.Browser.touch) {
+ L.DomEvent.disableClickPropagation(container);
+ L.DomEvent.on(container, 'mousewheel', L.DomEvent.stopPropagation);
+ } else {
+ L.DomEvent.on(container, 'click', L.DomEvent.stopPropagation);
+ }
+
+ var form = this._form = L.DomUtil.create('form', className + '-list');
+
+ if (this.options.collapsed) {
+ L.DomEvent
+ .on(container, 'mouseover', this._expand, this)
+ .on(container, 'mouseout', this._collapse, this);
+
+ var link = this._layersLink = L.DomUtil.create('a', className + '-toggle', container);
+ link.href = '#';
+ link.title = 'Layers';
+
+ if (L.Browser.touch) {
+ L.DomEvent
+ .on(link, 'click', L.DomEvent.stopPropagation)
+ .on(link, 'click', L.DomEvent.preventDefault)
+ .on(link, 'click', this._expand, this);
+ }
+ else {
+ L.DomEvent.on(link, 'focus', this._expand, this);
+ }
+
+ this._map.on('movestart', this._collapse, this);
+ // TODO keyboard accessibility
+ } else {
+ this._expand();
+ }
+
+ this._baseLayersList = L.DomUtil.create('div', className + '-base', form);
+ this._separator = L.DomUtil.create('div', className + '-separator', form);
+ this._overlaysList = L.DomUtil.create('div', className + '-overlays', form);
+
+ container.appendChild(form);
+ },
+
+ _addLayer: function (layer, name, overlay) {
+ var id = L.stamp(layer);
+
+ this._layers[id] = {
+ layer: layer,
+ name: name,
+ overlay: overlay
+ };
+
+ if (this.options.autoZIndex && layer.setZIndex) {
+ this._lastZIndex++;
+ layer.setZIndex(this._lastZIndex);
+ }
+ },
+
+ _update: function () {
+ if (!this._container) {
+ return;
+ }
+
+ this._baseLayersList.innerHTML = '';
+ this._overlaysList.innerHTML = '';
+
+ var baseLayersPresent = false,
+ overlaysPresent = false;
+
+ for (var i in this._layers) {
+ if (this._layers.hasOwnProperty(i)) {
+ var obj = this._layers[i];
+ this._addItem(obj);
+ overlaysPresent = overlaysPresent || obj.overlay;
+ baseLayersPresent = baseLayersPresent || !obj.overlay;
+ }
+ }
+
+ this._separator.style.display = (overlaysPresent && baseLayersPresent ? '' : 'none');
+ },
+
+ _onLayerChange: function (e) {
+ var id = L.stamp(e.layer);
+
+ if (this._layers[id] && !this._handlingClick) {
+ this._update();
+ }
+ },
+
+ // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe)
+ _createRadioElement: function (name, checked) {
+
+ var radioHtml = ' ';
+
+ var radioFragment = document.createElement('div');
+ radioFragment.innerHTML = radioHtml;
+
+ return radioFragment.firstChild;
+ },
+
+ _addItem: function (obj) {
+ var label = document.createElement('label'),
+ input,
+ checked = this._map.hasLayer(obj.layer);
+
+ if (obj.overlay) {
+ input = document.createElement('input');
+ input.type = 'checkbox';
+ input.className = 'leaflet-control-layers-selector';
+ input.defaultChecked = checked;
+ } else {
+ input = this._createRadioElement('leaflet-base-layers', checked);
+ }
+
+ input.layerId = L.stamp(obj.layer);
+
+ L.DomEvent.on(input, 'click', this._onInputClick, this);
+
+ var name = document.createElement('span');
+ name.innerHTML = ' ' + obj.name;
+
+ label.appendChild(input);
+ label.appendChild(name);
+
+ var container = obj.overlay ? this._overlaysList : this._baseLayersList;
+ container.appendChild(label);
+
+ return label;
+ },
+
+ _onInputClick: function () {
+ var i, input, obj,
+ inputs = this._form.getElementsByTagName('input'),
+ inputsLen = inputs.length,
+ baseLayer;
+
+ this._handlingClick = true;
+
+ for (i = 0; i < inputsLen; i++) {
+ input = inputs[i];
+ obj = this._layers[input.layerId];
+
+ if (input.checked && !this._map.hasLayer(obj.layer)) {
+ this._map.addLayer(obj.layer);
+ if (!obj.overlay) {
+ baseLayer = obj.layer;
+ }
+ } else if (!input.checked && this._map.hasLayer(obj.layer)) {
+ this._map.removeLayer(obj.layer);
+ }
+ }
+
+ if (baseLayer) {
+ this._map.setZoom(this._map.getZoom());
+ this._map.fire('baselayerchange', {layer: baseLayer});
+ }
+
+ this._handlingClick = false;
+ },
+
+ _expand: function () {
+ L.DomUtil.addClass(this._container, 'leaflet-control-layers-expanded');
+ },
+
+ _collapse: function () {
+ this._container.className = this._container.className.replace(' leaflet-control-layers-expanded', '');
+ }
+});
+
+L.control.layers = function (baseLayers, overlays, options) {
+ return new L.Control.Layers(baseLayers, overlays, options);
+};
+
+
+/*
+ * L.PosAnimation is used by Leaflet internally for pan animations.
+ */
+
+L.PosAnimation = L.Class.extend({
+ includes: L.Mixin.Events,
+
+ run: function (el, newPos, duration, easeLinearity) { // (HTMLElement, Point[, Number, Number])
+ this.stop();
+
+ this._el = el;
+ this._inProgress = true;
+
+ this.fire('start');
+
+ el.style[L.DomUtil.TRANSITION] = 'all ' + (duration || 0.25) +
+ 's cubic-bezier(0,0,' + (easeLinearity || 0.5) + ',1)';
+
+ L.DomEvent.on(el, L.DomUtil.TRANSITION_END, this._onTransitionEnd, this);
+ L.DomUtil.setPosition(el, newPos);
+
+ // toggle reflow, Chrome flickers for some reason if you don't do this
+ L.Util.falseFn(el.offsetWidth);
+
+ // there's no native way to track value updates of transitioned properties, so we imitate this
+ this._stepTimer = setInterval(L.bind(this.fire, this, 'step'), 50);
+ },
+
+ stop: function () {
+ if (!this._inProgress) { return; }
+
+ // if we just removed the transition property, the element would jump to its final position,
+ // so we need to make it stay at the current position
+
+ L.DomUtil.setPosition(this._el, this._getPos());
+ this._onTransitionEnd();
+ L.Util.falseFn(this._el.offsetWidth); // force reflow in case we are about to start a new animation
+ },
+
+ // you can't easily get intermediate values of properties animated with CSS3 Transitions,
+ // we need to parse computed style (in case of transform it returns matrix string)
+
+ _transformRe: /(-?[\d\.]+), (-?[\d\.]+)\)/,
+
+ _getPos: function () {
+ var left, top, matches,
+ el = this._el,
+ style = window.getComputedStyle(el);
+
+ if (L.Browser.any3d) {
+ matches = style[L.DomUtil.TRANSFORM].match(this._transformRe);
+ left = parseFloat(matches[1]);
+ top = parseFloat(matches[2]);
+ } else {
+ left = parseFloat(style.left);
+ top = parseFloat(style.top);
+ }
+
+ return new L.Point(left, top, true);
+ },
+
+ _onTransitionEnd: function () {
+ L.DomEvent.off(this._el, L.DomUtil.TRANSITION_END, this._onTransitionEnd, this);
+
+ if (!this._inProgress) { return; }
+ this._inProgress = false;
+
+ this._el.style[L.DomUtil.TRANSITION] = '';
+
+ clearInterval(this._stepTimer);
+
+ this.fire('step').fire('end');
+ }
+
+});
+
+
+/*
+ * Extends L.Map to handle panning animations.
+ */
+
+L.Map.include({
+
+ setView: function (center, zoom, forceReset) {
+ zoom = this._limitZoom(zoom);
+
+ var zoomChanged = (this._zoom !== zoom);
+
+ if (this._loaded && !forceReset && this._layers) {
+
+ if (this._panAnim) {
+ this._panAnim.stop();
+ }
+
+ var done = (zoomChanged ?
+ this._zoomToIfClose && this._zoomToIfClose(center, zoom) :
+ this._panByIfClose(center));
+
+ // exit if animated pan or zoom started
+ if (done) {
+ clearTimeout(this._sizeTimer);
+ return this;
+ }
+ }
+
+ // reset the map view
+ this._resetView(center, zoom);
+
+ return this;
+ },
+
+ panBy: function (offset, duration, easeLinearity) {
+ offset = L.point(offset);
+
+ if (!(offset.x || offset.y)) {
+ return this;
+ }
+
+ if (!this._panAnim) {
+ this._panAnim = new L.PosAnimation();
+
+ this._panAnim.on({
+ 'step': this._onPanTransitionStep,
+ 'end': this._onPanTransitionEnd
+ }, this);
+ }
+
+ this.fire('movestart');
+
+ L.DomUtil.addClass(this._mapPane, 'leaflet-pan-anim');
+
+ var newPos = L.DomUtil.getPosition(this._mapPane).subtract(offset)._round();
+ this._panAnim.run(this._mapPane, newPos, duration || 0.25, easeLinearity);
+
+ return this;
+ },
+
+ _onPanTransitionStep: function () {
+ this.fire('move');
+ },
+
+ _onPanTransitionEnd: function () {
+ L.DomUtil.removeClass(this._mapPane, 'leaflet-pan-anim');
+ this.fire('moveend');
+ },
+
+ _panByIfClose: function (center) {
+ // difference between the new and current centers in pixels
+ var offset = this._getCenterOffset(center)._floor();
+
+ if (this._offsetIsWithinView(offset)) {
+ this.panBy(offset);
+ return true;
+ }
+ return false;
+ },
+
+ _offsetIsWithinView: function (offset, multiplyFactor) {
+ var m = multiplyFactor || 1,
+ size = this.getSize();
+
+ return (Math.abs(offset.x) <= size.x * m) &&
+ (Math.abs(offset.y) <= size.y * m);
+ }
+});
+
+
+/*
+ * L.PosAnimation fallback implementation that powers Leaflet pan animations
+ * in browsers that don't support CSS3 Transitions.
+ */
+
+L.PosAnimation = L.DomUtil.TRANSITION ? L.PosAnimation : L.PosAnimation.extend({
+
+ run: function (el, newPos, duration, easeLinearity) { // (HTMLElement, Point[, Number, Number])
+ this.stop();
+
+ this._el = el;
+ this._inProgress = true;
+ this._duration = duration || 0.25;
+ this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2);
+
+ this._startPos = L.DomUtil.getPosition(el);
+ this._offset = newPos.subtract(this._startPos);
+ this._startTime = +new Date();
+
+ this.fire('start');
+
+ this._animate();
+ },
+
+ stop: function () {
+ if (!this._inProgress) { return; }
+
+ this._step();
+ this._complete();
+ },
+
+ _animate: function () {
+ // animation loop
+ this._animId = L.Util.requestAnimFrame(this._animate, this);
+ this._step();
+ },
+
+ _step: function () {
+ var elapsed = (+new Date()) - this._startTime,
+ duration = this._duration * 1000;
+
+ if (elapsed < duration) {
+ this._runFrame(this._easeOut(elapsed / duration));
+ } else {
+ this._runFrame(1);
+ this._complete();
+ }
+ },
+
+ _runFrame: function (progress) {
+ var pos = this._startPos.add(this._offset.multiplyBy(progress));
+ L.DomUtil.setPosition(this._el, pos);
+
+ this.fire('step');
+ },
+
+ _complete: function () {
+ L.Util.cancelAnimFrame(this._animId);
+
+ this._inProgress = false;
+ this.fire('end');
+ },
+
+ _easeOut: function (t) {
+ return 1 - Math.pow(1 - t, this._easeOutPower);
+ }
+});
+
+
+/*
+ * Extends L.Map to handle zoom animations.
+ */
+
+L.Map.mergeOptions({
+ zoomAnimation: L.DomUtil.TRANSITION && !L.Browser.android23 && !L.Browser.mobileOpera
+});
+
+if (L.DomUtil.TRANSITION) {
+ L.Map.addInitHook(function () {
+ L.DomEvent.on(this._mapPane, L.DomUtil.TRANSITION_END, this._catchTransitionEnd, this);
+ });
+}
+
+L.Map.include(!L.DomUtil.TRANSITION ? {} : {
+
+ _zoomToIfClose: function (center, zoom) {
+
+ if (this._animatingZoom) { return true; }
+
+ if (!this.options.zoomAnimation) { return false; }
+
+ var scale = this.getZoomScale(zoom),
+ offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale);
+
+ // if offset does not exceed half of the view
+ if (!this._offsetIsWithinView(offset, 1)) { return false; }
+
+ L.DomUtil.addClass(this._mapPane, 'leaflet-zoom-anim');
+
+ this
+ .fire('movestart')
+ .fire('zoomstart');
+
+ this.fire('zoomanim', {
+ center: center,
+ zoom: zoom
+ });
+
+ var origin = this._getCenterLayerPoint().add(offset);
+
+ this._prepareTileBg();
+ this._runAnimation(center, zoom, scale, origin);
+
+ return true;
+ },
+
+ _catchTransitionEnd: function () {
+ if (this._animatingZoom) {
+ this._onZoomTransitionEnd();
+ }
+ },
+
+ _runAnimation: function (center, zoom, scale, origin, backwardsTransform) {
+ this._animateToCenter = center;
+ this._animateToZoom = zoom;
+ this._animatingZoom = true;
+
+ if (L.Draggable) {
+ L.Draggable._disabled = true;
+ }
+
+ var transform = L.DomUtil.TRANSFORM,
+ tileBg = this._tileBg;
+
+ clearTimeout(this._clearTileBgTimer);
+
+ L.Util.falseFn(tileBg.offsetWidth); //hack to make sure transform is updated before running animation
+
+ var scaleStr = L.DomUtil.getScaleString(scale, origin),
+ oldTransform = tileBg.style[transform];
+
+ tileBg.style[transform] = backwardsTransform ?
+ oldTransform + ' ' + scaleStr :
+ scaleStr + ' ' + oldTransform;
+ },
+
+ _prepareTileBg: function () {
+ var tilePane = this._tilePane,
+ tileBg = this._tileBg;
+
+ // If foreground layer doesn't have many tiles but bg layer does, keep the existing bg layer and just zoom it some more
+ if (tileBg && this._getLoadedTilesPercentage(tileBg) > 0.5 &&
+ this._getLoadedTilesPercentage(tilePane) < 0.5) {
+
+ tilePane.style.visibility = 'hidden';
+ tilePane.empty = true;
+ this._stopLoadingImages(tilePane);
+ return;
+ }
+
+ if (!tileBg) {
+ tileBg = this._tileBg = this._createPane('leaflet-tile-pane', this._mapPane);
+ tileBg.style.zIndex = 1;
+ }
+
+ // prepare the background pane to become the main tile pane
+ tileBg.style[L.DomUtil.TRANSFORM] = '';
+ tileBg.style.visibility = 'hidden';
+
+ // tells tile layers to reinitialize their containers
+ tileBg.empty = true; //new FG
+ tilePane.empty = false; //new BG
+
+ //Switch out the current layer to be the new bg layer (And vice-versa)
+ this._tilePane = this._panes.tilePane = tileBg;
+ var newTileBg = this._tileBg = tilePane;
+
+ L.DomUtil.addClass(newTileBg, 'leaflet-zoom-animated');
+
+ this._stopLoadingImages(newTileBg);
+ },
+
+ _getLoadedTilesPercentage: function (container) {
+ var tiles = container.getElementsByTagName('img'),
+ i, len, count = 0;
+
+ for (i = 0, len = tiles.length; i < len; i++) {
+ if (tiles[i].complete) {
+ count++;
+ }
+ }
+ return count / len;
+ },
+
+ // stops loading all tiles in the background layer
+ _stopLoadingImages: function (container) {
+ var tiles = Array.prototype.slice.call(container.getElementsByTagName('img')),
+ i, len, tile;
+
+ for (i = 0, len = tiles.length; i < len; i++) {
+ tile = tiles[i];
+
+ if (!tile.complete) {
+ tile.onload = L.Util.falseFn;
+ tile.onerror = L.Util.falseFn;
+ tile.src = L.Util.emptyImageUrl;
+
+ tile.parentNode.removeChild(tile);
+ }
+ }
+ },
+
+ _onZoomTransitionEnd: function () {
+ this._restoreTileFront();
+
+ L.DomUtil.removeClass(this._mapPane, 'leaflet-zoom-anim');
+ L.Util.falseFn(this._tileBg.offsetWidth); // force reflow
+ this._animatingZoom = false;
+ this._resetView(this._animateToCenter, this._animateToZoom, true, true);
+
+ if (L.Draggable) {
+ L.Draggable._disabled = false;
+ }
+ },
+
+ _restoreTileFront: function () {
+ this._tilePane.innerHTML = '';
+ this._tilePane.style.visibility = '';
+ this._tilePane.style.zIndex = 2;
+ this._tileBg.style.zIndex = 1;
+ },
+
+ _clearTileBg: function () {
+ if (!this._animatingZoom && !this.touchZoom._zooming) {
+ this._tileBg.innerHTML = '';
+ }
+ }
+});
+
+
+/*
+ * Provides L.Map with convenient shortcuts for using browser geolocation features.
+ */
+
+L.Map.include({
+ _defaultLocateOptions: {
+ watch: false,
+ setView: false,
+ maxZoom: Infinity,
+ timeout: 10000,
+ maximumAge: 0,
+ enableHighAccuracy: false
+ },
+
+ locate: function (/*Object*/ options) {
+
+ options = this._locationOptions = L.extend(this._defaultLocateOptions, options);
+
+ if (!navigator.geolocation) {
+ this._handleGeolocationError({
+ code: 0,
+ message: "Geolocation not supported."
+ });
+ return this;
+ }
+
+ var onResponse = L.bind(this._handleGeolocationResponse, this),
+ onError = L.bind(this._handleGeolocationError, this);
+
+ if (options.watch) {
+ this._locationWatchId =
+ navigator.geolocation.watchPosition(onResponse, onError, options);
+ } else {
+ navigator.geolocation.getCurrentPosition(onResponse, onError, options);
+ }
+ return this;
+ },
+
+ stopLocate: function () {
+ if (navigator.geolocation) {
+ navigator.geolocation.clearWatch(this._locationWatchId);
+ }
+ return this;
+ },
+
+ _handleGeolocationError: function (error) {
+ var c = error.code,
+ message = error.message ||
+ (c === 1 ? "permission denied" :
+ (c === 2 ? "position unavailable" : "timeout"));
+
+ if (this._locationOptions.setView && !this._loaded) {
+ this.fitWorld();
+ }
+
+ this.fire('locationerror', {
+ code: c,
+ message: "Geolocation error: " + message + "."
+ });
+ },
+
+ _handleGeolocationResponse: function (pos) {
+ var latAccuracy = 180 * pos.coords.accuracy / 4e7,
+ lngAccuracy = latAccuracy * 2,
+
+ lat = pos.coords.latitude,
+ lng = pos.coords.longitude,
+ latlng = new L.LatLng(lat, lng),
+
+ sw = new L.LatLng(lat - latAccuracy, lng - lngAccuracy),
+ ne = new L.LatLng(lat + latAccuracy, lng + lngAccuracy),
+ bounds = new L.LatLngBounds(sw, ne),
+
+ options = this._locationOptions;
+
+ if (options.setView) {
+ var zoom = Math.min(this.getBoundsZoom(bounds), options.maxZoom);
+ this.setView(latlng, zoom);
+ }
+
+ this.fire('locationfound', {
+ latlng: latlng,
+ bounds: bounds,
+ accuracy: pos.coords.accuracy
+ });
+ }
+});
+
+
+}(this, document));
diff --git a/chimere/templates/chimere/base.html b/chimere/templates/chimere/base.html
index 620c84f..0a5818f 100644
--- a/chimere/templates/chimere/base.html
+++ b/chimere/templates/chimere/base.html
@@ -3,8 +3,8 @@
{% block extra_head %}
-{% if css_area %}
- {% endif %}
+{% if css_map %}
+ {% endif %}
{% endblock %}
{% block header %}
diff --git a/chimere/templates/chimere/blocks/areas.html b/chimere/templates/chimere/blocks/areas.html
deleted file mode 100644
index a1ebe76..0000000
--- a/chimere/templates/chimere/blocks/areas.html
+++ /dev/null
@@ -1,25 +0,0 @@
-{% load i18n %}
-{% if areas and areas.count > 1 %}
-
- {% trans "Areas:" %}
-
- {% if not has_default_area %}-- {% endif %}
- {% for area in areas %}
- {{area.name}}
- {% endfor %}
-
-
-
-{% endif %}
diff --git a/chimere/templates/chimere/blocks/areas.html b/chimere/templates/chimere/blocks/areas.html
new file mode 120000
index 0000000..0883da9
--- /dev/null
+++ b/chimere/templates/chimere/blocks/areas.html
@@ -0,0 +1 @@
+maps.html
\ No newline at end of file
diff --git a/chimere/templates/chimere/blocks/areas_alternative.html b/chimere/templates/chimere/blocks/areas_alternative.html
deleted file mode 100644
index 837bf45..0000000
--- a/chimere/templates/chimere/blocks/areas_alternative.html
+++ /dev/null
@@ -1,11 +0,0 @@
-{% load i18n unlocalize_point %}
-{% if areas %}
-
-
{% trans "Shortcuts"%}
-
- {% for area in areas %}
- {{area.name}}
- {% endfor %}
-
-
-{% endif %}
diff --git a/chimere/templates/chimere/blocks/head_chimere.html b/chimere/templates/chimere/blocks/head_chimere.html
index c1cdcc9..12a468a 100644
--- a/chimere/templates/chimere/blocks/head_chimere.html
+++ b/chimere/templates/chimere/blocks/head_chimere.html
@@ -20,8 +20,8 @@
var CENTER_LONLAT = centerLonLat = new OpenLayers.LonLat{{ DEFAULT_CENTER }}.transform(epsg_display_projection, epsg_projection);
{% endifequal %}
var restricted_extent;
- {% if area_name %}
- var area_name = '{{ area_name }}';{% endif %}
+ {% if map_name %}
+ var map_name = '{{ map_name }}';{% endif %}
$(function() {$( ".draggable" ).draggable({handle:"h2"});});
var get_share_url = '{% url chimere:get-share-url %}';
diff --git a/chimere/templates/chimere/blocks/live_coordinates.html b/chimere/templates/chimere/blocks/live_coordinates.html
index 0199630..4392dca 100644
--- a/chimere/templates/chimere/blocks/live_coordinates.html
+++ b/chimere/templates/chimere/blocks/live_coordinates.html
@@ -12,7 +12,7 @@
chimere_init_options['dynamic_categories'] = false;
chimere_init_options['edition'] = true;
chimere_init_options["checked_categories"] = [];
- if({{default_area}}) chimere_init_options['selected_map_layer'] = {{default_area}};
+ if({{default_map}}) chimere_init_options['selected_map_layer'] = {{default_map}};
diff --git a/chimere/templates/chimere/blocks/map.html b/chimere/templates/chimere/blocks/map.html
index 2c225de..e3b94d9 100644
--- a/chimere/templates/chimere/blocks/map.html
+++ b/chimere/templates/chimere/blocks/map.html
@@ -26,8 +26,8 @@
chimere_init_options['permalink_element'] = document.getElementById('permalink');
chimere_init_options['routing'] = {{routing}};
{% if dynamic_categories %}chimere_init_options['dynamic_categories'] = true;{% endif %}
- {% if default_area %}
- chimere_init_options["default_area"] = new Array({{default_area.upper_left_corner.x}}, {{default_area.upper_left_corner.y}}, {{default_area.lower_right_corner.x}}, {{default_area.lower_right_corner.y}});
+ {% if default_map %}
+ chimere_init_options["default_map"] = new Array({{default_map.upper_left_corner.x}}, {{default_map.upper_left_corner.y}}, {{default_map.lower_right_corner.x}}, {{default_map.lower_right_corner.y}});
{% endif %}
{% if p_zoom %}chimere_init_options["zoom"] = {{ p_zoom }};{% endif %}
{% if p_lat %}chimere_init_options["lat"] = {{ p_lat }};{% endif %}
@@ -43,7 +43,7 @@
chimere_init_options["dynamic_categories"] = {{ dynamic_categories }};
{% if p_display_submited %}chimere_init_options["display_submited"] = {{ p_display_submited }};{% endif %}
chimere_init_options["checked_categories"] = [{% for cc in checked_categories %}{% if forloop.counter0 > 0 %}, {% endif %}{{cc}}{% endfor %}];
- {% if area_id %}chimere_init_options['area_id'] = "{{area_id}}";{% endif %}
+ {% if map_id %}chimere_init_options['map_id'] = "{{map_id}}";{% endif %}
{% if p_current_feature %}
chimere_init_options["display_feature"] = {{ p_current_feature }};{% endif %}
{% if p_current_route %}
@@ -55,9 +55,9 @@
{% if selected_map_layer %}
chimere_init_options["selected_map_layer"] = {{selected_map_layer}};
{% endif %}
- $("#main-map").chimere(chimere_init_options);
+ $("#{{map_id}}").chimere(chimere_init_options);
{% if zoom %}
- $('#maps').chimere('zoom', {'area':{{zoom}} });
+ $('#{{map_id}}').chimere('zoom', {'area':{{zoom}} });
{% endif %}
// init layer selection
diff --git a/chimere/templates/chimere/blocks/maps.html b/chimere/templates/chimere/blocks/maps.html
new file mode 100644
index 0000000..b069660
--- /dev/null
+++ b/chimere/templates/chimere/blocks/maps.html
@@ -0,0 +1,25 @@
+{% load i18n %}
+{% if maps and maps.count > 1 %}
+
+ {% trans "Maps:" %}
+
+ {% if not has_default_map %}-- {% endif %}
+ {% for map in maps %}
+ {{map.name}}
+ {% endfor %}
+
+
+
+{% endif %}
diff --git a/chimere/templates/chimere/blocks/maps_alternative.html b/chimere/templates/chimere/blocks/maps_alternative.html
new file mode 100644
index 0000000..c91d75a
--- /dev/null
+++ b/chimere/templates/chimere/blocks/maps_alternative.html
@@ -0,0 +1,11 @@
+{% load i18n unlocalize_point %}
+{% if maps %}
+
+
{% trans "Shortcuts"%}
+
+ {% for map in maps %}
+ {{map.name}}
+ {% endfor %}
+
+
+{% endif %}
diff --git a/chimere/templates/chimere/blocks/news.html b/chimere/templates/chimere/blocks/news.html
index fa581f7..07c2bbb 100644
--- a/chimere/templates/chimere/blocks/news.html
+++ b/chimere/templates/chimere/blocks/news.html
@@ -39,7 +39,7 @@ $(function(){
{% for property in news.getProperties %}
{{ property.value|sanitize:"p b i br hr strong em span:style a:href:target ul li ol h1 h2 h3 h4 table td th tr"|safe }}
{% endfor %}
- {% trans "See it on the map"%}
+ {% trans "See it on the map"%}
{% endif %}
{%endfor%}
diff --git a/chimere/templates/chimere/blocks/welcome.html b/chimere/templates/chimere/blocks/welcome.html
index 3ec2ccf..a0052f5 100644
--- a/chimere/templates/chimere/blocks/welcome.html
+++ b/chimere/templates/chimere/blocks/welcome.html
@@ -49,7 +49,7 @@ $(function(){
{% for property in news.getProperties %}
{{ property.value|sanitize:"p b i br hr strong em img:src:alt span:style a:href:target ul li ol h1 h2 h3 h4 table td th tr"|safe }}
{% endfor %}
- {% trans "See it on the map"%}
+ {% trans "See it on the map"%}
{% endif %}
{%endfor%}
diff --git a/chimere/templates/chimere/detail.html b/chimere/templates/chimere/detail.html
index ed0ea5c..f547785 100644
--- a/chimere/templates/chimere/detail.html
+++ b/chimere/templates/chimere/detail.html
@@ -28,7 +28,7 @@
{% trans "Show multimedia gallery" %}
{% endif %}
-
+
{% trans "Submit an amendment" %}
{% if moderator_emails %}
diff --git a/chimere/templates/chimere/feeds/rss.html b/chimere/templates/chimere/feeds/rss.html
index 53b0a49..df8bb26 100644
--- a/chimere/templates/chimere/feeds/rss.html
+++ b/chimere/templates/chimere/feeds/rss.html
@@ -46,13 +46,13 @@
{%ifequal category_rss_feed "area" %}
{% trans "New points of interest by area" %}
-{% if area_id %}
+{% if map_id %}
-
{% trans "Choose a pre-defined areas" %}
-
+ {% trans "Choose a pre-defined map" %}
+
----
- {% for areaID in area_id %}
- {{areaID.name}}
+ {% for mapID in map_id %}
+ {{mapID.name}}
{% endfor %}
diff --git a/chimere/templates/chimere/main_map.html b/chimere/templates/chimere/main_map.html
index e8bdfe1..4187440 100644
--- a/chimere/templates/chimere/main_map.html
+++ b/chimere/templates/chimere/main_map.html
@@ -24,14 +24,14 @@
{% endblock %}
diff --git a/chimere/templatetags/chimere_tags.py b/chimere/templatetags/chimere_tags.py
index fd700d9..dbb9efc 100644
--- a/chimere/templatetags/chimere_tags.py
+++ b/chimere/templatetags/chimere_tags.py
@@ -14,23 +14,23 @@ from django.template import defaultfilters
from django.utils.translation import ugettext as _
from django.template.loader import render_to_string
-from chimere.models import Marker, Area, News, SubCategory, MultimediaType
+from chimere.models import Marker, Map, News, SubCategory, MultimediaType
from chimere.widgets import get_map_layers
log = getLogger(__name__)
register = template.Library()
-@register.inclusion_tag('chimere/blocks/areas.html', takes_context=True)
-def display_areas(context):
+@register.inclusion_tag('chimere/blocks/maps.html', takes_context=True)
+def display_maps(context):
"""
- Display available areas.
+ Display available maps.
"""
- context_data = {"areas": Area.getAvailable(),
+ context_data = {"maps": Map.getAvailable(),
"base_url": reverse('chimere:index')
}
- if "area_name" in context:
- context_data['area_name'] = context["area_name"]
+ if "map_name" in context:
+ context_data['map_name'] = context["map_name"]
return context_data
@register.inclusion_tag('chimere/blocks/submited.html', takes_context=True)
@@ -41,11 +41,11 @@ def submited(context):
return {"edit_url":reverse('chimere:edit'),
"index_url":reverse('chimere:index')}
-def get_news(area=None):
+def get_news(map=None):
# Retrieve news
news = News.objects.filter(available=True, is_front_page=True)
- if area:
- news = news.filter(Q(areas__isnull=True)|Q(areas__in=[area.pk]))
+ if map:
+ news = news.filter(Q(maps__isnull=True)|Q(maps__in=[map.pk]))
news = list(news.all())
if settings.CHIMERE_DAYS_BEFORE_EVENT:
# Retrieve active markers
@@ -54,8 +54,8 @@ def get_news(area=None):
q = Marker.objects.filter(status='A').filter(start_date__lte=after,
is_front_page=True).filter(Q(end_date__gte=today)|
(Q(end_date__isnull=True) & Q(start_date__gte=today)))
- if area:
- q = q.filter(area.getIncludeMarker())
+ if map:
+ q = q.filter(map.getIncludeMarker())
news += list(q)
news.sort(key=lambda x:x.date, reverse=True)
return news
@@ -66,15 +66,15 @@ def display_welcome(context, display=False, title=''):
Welcome message and active news.
"""
context_data = {'display':display}
- area = None
- if "area_name" in context:
+ map = None
+ if "map_name" in context:
try:
- area = Area.objects.get(urn=context["area_name"])
- context_data['area_name'] = context['area_name']
- context_data['welcome_message'] = area.welcome_message
+ map = Map.objects.get(urn=context["map_name"])
+ context_data['map_name'] = context['map_name']
+ context_data['welcome_message'] = map.welcome_message
except ObjectDoesNotExist:
pass
- context_data['news_lst'] = get_news(area)[:3]
+ context_data['news_lst'] = get_news(map)[:3]
context_data['STATIC_URL'] = settings.STATIC_URL
context_data['title'] = title if title \
else _(u"Welcome to the %s") % settings.PROJECT_NAME
@@ -85,16 +85,16 @@ def display_news(context, title=''):
"""
All news.
"""
- area = None
+ map = None
context_data = {'STATIC_URL':settings.STATIC_URL}
- if "area_name" in context:
+ if "map_name" in context:
try:
- area = Area.objects.get(urn=context["area_name"])
- context_data['area_name'] = context['area_name']
- context_data['welcome_message'] = area.welcome_message
+ map = Map.objects.get(urn=context["map_name"])
+ context_data['map_name'] = context['map_name']
+ context_data['welcome_message'] = map.welcome_message
except ObjectDoesNotExist:
pass
- context_data['news_lst'] = get_news(area)
+ context_data['news_lst'] = get_news(map)
return context_data
@register.inclusion_tag('chimere/blocks/head_jquery.html', takes_context=True)
@@ -123,11 +123,11 @@ def head_chimere(context, view=True):
"""
Create context and display head elements (js, css, etc.) for chimere.
"""
- area_name = context['area_name'] if 'area_name' in context else 'area_name'
- area = None
- if area_name:
+ map_name = context['map_name'] if 'map_name' in context else 'map_name'
+ map = None
+ if map_name:
try:
- area = Area.objects.get(urn=area_name)
+ map = Map.objects.get(urn=map_name)
except ObjectDoesNotExist:
pass
map_renderer = settings.CHIMERE_VIEW_RENDERER if view \
@@ -135,7 +135,7 @@ def head_chimere(context, view=True):
context_data = {
"STATIC_URL": settings.STATIC_URL,
"MEDIA_URL": settings.MEDIA_URL,
- "DYNAMIC_CATEGORIES": 'true' if area and area.dynamic_categories \
+ "DYNAMIC_CATEGORIES": 'true' if map and map.dynamic_categories \
else 'false',
"EXTRA_URL": reverse("chimere:index"),
"EPSG_DISPLAY_PROJECTION": settings.CHIMERE_EPSG_DISPLAY_PROJECTION,
@@ -198,51 +198,51 @@ def map(context, map_id='map'):
if hasattr(settings, 'CHIMERE_ENABLE_ROUTING') and \
settings.CHIMERE_ENABLE_ROUTING \
else 'false'
- area_name = context['area_name'] if 'area_name' in context else 'area_name'
- map_layers, default_area = get_map_layers(area_name)
+ map_name = context['map_name'] if 'map_name' in context else 'map_name'
+ map_layers, default_map = get_map_layers(map_name)
context_data['js_map_layers'] = ", ".join(
[js for name, js, default in map_layers])
context_data['js_map_layer_names'] = '"'+ '", "'.join(
[name for name, js, default in map_layers]) + '"'
context_data['map_layers'] = map_layers
- if default_area:
- context_data['selected_map_layer'] = default_area
+ if default_map:
+ context_data['selected_map_layer'] = default_map
context_data['checked_categories'] = []
- area = None
- if area_name:
+ map = None
+ if map_name:
try:
- area = Area.objects.get(urn=area_name)
+ map = Map.objects.get(urn=map_name)
except ObjectDoesNotExist:
pass
- if not area:
+ if not map:
try:
- area = Area.objects.get(default=True)
+ map = Map.objects.get(default=True)
except ObjectDoesNotExist:
pass
subcat_layer = SubCategory.objects.filter(as_layer=True, available=True)
- if area:
- if area.subcategories.count():
- subcat_layer = subcat_layer.filter(areas__pk=area.pk)
- context_data['area_id'] = area_name
+ if map:
+ if map.subcategories.count():
+ subcat_layer = subcat_layer.filter(maps__pk=map.pk)
+ context_data['map_id'] = map_name
if 'zoomout' in context and context['zoomout']:
context_data['zoom'] = "[%s]" % ",".join((
- unicode(area.upper_left_corner.x),
- unicode(area.upper_left_corner.y),
- unicode(area.lower_right_corner.x),
- unicode(area.lower_right_corner.y)))
- if area.subcategories.filter(available=True).count() == 1:
+ unicode(map.upper_left_corner.x),
+ unicode(map.upper_left_corner.y),
+ unicode(map.lower_right_corner.x),
+ unicode(map.lower_right_corner.y)))
+ if map.subcategories.filter(available=True).count() == 1:
context_data['single_category'] = True
- context_data['checked_categories'] = area.subcategories.all()[0].pk
- elif area.default_subcategories.count():
+ context_data['checked_categories'] = map.subcategories.all()[0].pk
+ elif map.default_subcategories.count():
context_data['checked_categories'] = [s.pk
- for s in area.default_subcategories.all()]
- if area.restrict_to_extent:
+ for s in map.default_subcategories.all()]
+ if map.restrict_to_extent:
context_data['restricted_extent'] = """
var bounds = new OpenLayers.Bounds();
bounds.extend(new OpenLayers.LonLat(%f, %f));
bounds.extend(new OpenLayers.LonLat(%f, %f));
-""" % (area.upper_left_corner.x, area.upper_left_corner.y,
- area.lower_right_corner.x, area.lower_right_corner.y)
+""" % (map.upper_left_corner.x, map.upper_left_corner.y,
+ map.lower_right_corner.x, map.lower_right_corner.y)
context_data['subcat_layer'], c_cat = [], None
for subcat in subcat_layer.order_by('category__order', 'category').all():
if subcat.category != c_cat:
@@ -258,7 +258,7 @@ bounds.extend(new OpenLayers.LonLat(%f, %f));
).all()[0].pk)
context_data['checked_categories'] = cat
context_data['dynamic_categories'] = 'true' \
- if area and area.dynamic_categories else 'false'
+ if map and map.dynamic_categories else 'false'
if 'request' not in context:
return context_data
request = context['request']
@@ -291,10 +291,10 @@ def alternate_multimedia(formset_multi, formset_picture):
'auto_type_id':MultimediaType.objects.get(name='auto').pk}
@register.simple_tag
-def get_tinyfied_url(marker, area_name=''):
+def get_tinyfied_url(marker, map_name=''):
if not marker or not hasattr(marker, 'get_absolute_url'):
return ""
- url = marker.get_absolute_url(area_name)
+ url = marker.get_absolute_url(map_name)
return url
@register.inclusion_tag('chimere/blocks/share_bar.html',
diff --git a/chimere/urls.py b/chimere/urls.py
index b0c01d7..36275ab 100644
--- a/chimere/urls.py
+++ b/chimere/urls.py
@@ -23,7 +23,7 @@ from django.contrib import admin
from django.core.exceptions import ImproperlyConfigured
admin.autodiscover()
-from chimere.models import Area
+from chimere.models import Map
from chimere.feeds import LatestPOIsByCategory, LatestPOIsBySubCategory, \
LatestPOIs, LatestPOIsByZone, LatestPOIsByZoneID
@@ -32,24 +32,24 @@ def i18n_javascript(request):
urlpatterns = patterns('chimere.views',
- url(r'^(?P[a-zA-Z0-9_-]+/)?simple$', 'index', {'simple':True},
+ url(r'^(?P[a-zA-Z0-9_-]+/)?simple$', 'index', {'simple':True},
name="simple_index")
)
if settings.CHIMERE_FEEDS:
urlpatterns += patterns('',
- url(r'^(?P[a-zA-Z0-9_-]+/)?feeds$', 'chimere.views.rss',
+ url(r'^(?P[a-zA-Z0-9_-]+/)?feeds$', 'chimere.views.rss',
name='feeds-form'),
- url(r'^(?P[a-zA-Z0-9_-]+/)?feeds/category/(?P\d+)$',
+ url(r'^(?P[a-zA-Z0-9_-]+/)?feeds/category/(?P\d+)$',
LatestPOIsByCategory(), name='feeds-cat'),
- url(r'^(?P[a-zA-Z0-9_-]+/)?feeds/subcategory/(?P\d+)$',
+ url(r'^(?P[a-zA-Z0-9_-]+/)?feeds/subcategory/(?P\d+)$',
LatestPOIsBySubCategory(), name='feeds-subcat'),
- url(r'^(?P[a-zA-Z0-9_-]+/)?feeds/global/$', LatestPOIs(),
+ url(r'^(?P[a-zA-Z0-9_-]+/)?feeds/global/$', LatestPOIs(),
name='feeds-global'),
- url(r'^(?P[a-zA-Z0-9_-]+/)?feeds/area/(?P [0-9-_.]+)$',
+ url(r'^(?P[a-zA-Z0-9_-]+/)?feeds/area/(?P [0-9-_.]+)$',
LatestPOIsByZone(), name='feeds-area'),
- url(r'^(?P[a-zA-Z0-9_-]+/)?feeds/areaid/(?P\d+)$',
- LatestPOIsByZoneID(), name='feeds-areaid'),
+ url(r'^(?P[a-zA-Z0-9_-]+/)?feeds/mapid/(?P\d+)$',
+ LatestPOIsByZoneID(), name='feeds-mapid'),
)
if hasattr(settings, 'CHIMERE_ENABLE_ROUTING') \
@@ -59,7 +59,7 @@ if hasattr(settings, 'CHIMERE_ENABLE_ROUTING') \
raise ImproperlyConfigured(u"CHIMERE_ROUTING_TRANSPORT must be set in"\
u" settings if you enable routing")
urlpatterns += patterns('chimere.views',
- url(r'^(?P[a-zA-Z0-9_-]*/)?route/'\
+ url(r'^(?P[a-zA-Z0-9_-]*/)?route/'\
r'(?P(%s))/((?P[0-9][0-9]*)/)?'
r'(?P[-]?[0-9]+[.]?[0-9]*)_(?P[-]?[0-9]+[.]?[0-9]*)_'\
r'(?P([-]?[0-9]+[.]?[0-9]*_[-]?[0-9]+[.]?[0-9]*_)*)'\
@@ -70,41 +70,41 @@ if hasattr(settings, 'CHIMERE_ENABLE_ROUTING') \
urlpatterns += patterns('chimere.views',
url(r'^charte/?$', 'charte', name="charte"),
- url(r'^(?P[a-zA-Z0-9_-]+/)?contact/?$', 'contactus',
+ url(r'^(?P[a-zA-Z0-9_-]+/)?contact/?$', 'contactus',
name="contact"),
- url(r'^(?P[a-zA-Z0-9_-]+/)?edit/$', 'edit',
+ url(r'^(?P[a-zA-Z0-9_-]+/)?edit/$', 'edit',
name="edit"),
- url(r'^(?P[a-zA-Z0-9_-]+/)?edit/(?P\w+)/(?P\w+)?$',
+ url(r'^(?P[a-zA-Z0-9_-]+/)?edit/(?P\w+)/(?P\w+)?$',
'edit', name="edit-item"),
- url(r'^(?P[a-zA-Z0-9_-]+/)?edit-route/$', 'editRoute',
+ url(r'^(?P[a-zA-Z0-9_-]+/)?edit-route/$', 'editRoute',
name="editroute"),
- url(r'^(?P[a-zA-Z0-9_-]+/)?edit-route/(?P\w+)/(?P\w+)?$',
+ url(r'^(?P[a-zA-Z0-9_-]+/)?edit-route/(?P\w+)/(?P\w+)?$',
'editRoute', name="editroute-item"),
- url(r'^(?P[a-zA-Z0-9_-]+/)?getDetail/(?P\d+)/?$', 'getDetail',
+ url(r'^(?P[a-zA-Z0-9_-]+/)?getDetail/(?P\d+)/?$', 'getDetail',
name="get_detail"),
- url(r'^(?P[a-zA-Z0-9_-]+/)?getDescriptionDetail/?(?P\d+)/?$',
+ url(r'^(?P[a-zA-Z0-9_-]+/)?getDescriptionDetail/?(?P\d+)/?$',
'getDescriptionDetail', name="get_description_detail"),
- url(r'^(?P[a-zA-Z0-9_-]+/)?getGeoObjects/'\
+ url(r'^(?P[a-zA-Z0-9_-]+/)?getGeoObjects/'\
r'(?P[a-zA-Z0-9_-]+)(/(?P\w+))?$', 'getGeoObjects',
name="getgeoobjects"),
- url(r'^(?P[a-zA-Z0-9_-]+/)?getAvailableCategories/$',
+ url(r'^(?P[a-zA-Z0-9_-]+/)?getAvailableCategories/$',
'get_available_categories', name="get_categories"),
- url(r'^(?P[a-zA-Z0-9_-]+/)?getAllCategories/$',
+ url(r'^(?P[a-zA-Z0-9_-]+/)?getAllCategories/$',
'get_all_categories', name="get_all_categories"),
- url(r'^(?P[a-zA-Z0-9_-]+/)?getCategory/(?P\d+)/?$',
+ url(r'^(?P[a-zA-Z0-9_-]+/)?getCategory/(?P\d+)/?$',
'getCategory', name="get_category"),
- url(r'^(?P[a-zA-Z0-9_-]*/)?get-share-url/(?P\w+)?$',
+ url(r'^(?P[a-zA-Z0-9_-]*/)?get-share-url/(?P\w+)?$',
'getShareUrl', name="get-share-url"),
- url(r'^(?P[a-zA-Z0-9_-]*/)?ty/(?P\w+)$',
+ url(r'^(?P[a-zA-Z0-9_-]*/)?ty/(?P\w+)$',
'redirectFromTinyURN', name="tiny"),
- url(r'^(?P[a-zA-Z0-9_-]+/)?upload_file/((?P\w+)/)?$',
+ url(r'^(?P[a-zA-Z0-9_-]+/)?upload_file/((?P\w+)/)?$',
'uploadFile', name='upload_file'),
- url(r'^(?P[a-zA-Z0-9_-]+/)?process_route_file/(?P\d+)/$',
+ url(r'^(?P[a-zA-Z0-9_-]+/)?process_route_file/(?P\d+)/$',
'processRouteFile', name='process_route_file'),
- url(r'^(?P[a-zA-Z0-9_-]+/)?dyn/(?P\w+)/$',
+ url(r'^(?P[a-zA-Z0-9_-]+/)?dyn/(?P\w+)/$',
'extraPage', name='extra_page'),
- url(r'^(?P[a-zA-Z0-9_-]+/)?json/(?P[a-zA-Z0-9_-]+)/(?P[a-zA-Z0-9_-]+).json$', 'get_json',
+ url(r'^(?P[a-zA-Z0-9_-]+/)?json/(?P[a-zA-Z0-9_-]+)/(?P[a-zA-Z0-9_-]+).json$', 'get_json',
name='get-json'),
# At the end, because it catches large
- url(r'^(?P[a-zA-Z0-9_-]+)?', 'index', name="index"),
+ url(r'^(?P[a-zA-Z0-9_-]+)?', 'index', name="index"),
)
diff --git a/chimere/views.py b/chimere/views.py
index f58b7ec..f48a3b1 100644
--- a/chimere/views.py
+++ b/chimere/views.py
@@ -45,14 +45,14 @@ from django.utils.translation import ugettext as _
from chimere.actions import actions
from chimere.models import Category, SubCategory, PropertyModel, Page,\
- Marker, Route, News, SimpleArea, Area, Color, TinyUrl, RouteFile,\
+ Marker, Route, News, SimpleArea, Map, Color, TinyUrl, RouteFile,\
AggregatedRoute
from chimere.widgets import getMapJS, PointChooserWidget, \
RouteChooserWidget, AreaWidget
from chimere.forms import MarkerForm, RouteForm, ContactForm, FileForm, \
FullFileForm, MultimediaFileFormSet, PictureFileFormSet, notifySubmission,\
- notifyStaff, AreaForm, RoutingForm, getStaffEmails
+ notifyStaff, MapForm, RoutingForm, getStaffEmails
from chimere.route import router
@@ -69,7 +69,7 @@ def get_base_uri(request):
return base_uri
#TODO: convert to requestcontext
-def get_base_response(area_name=""):
+def get_base_response(map_name=""):
"""
Get the base url
"""
@@ -77,49 +77,49 @@ def get_base_response(area_name=""):
base_url = reverse("chimere:index")
if not base_url.startswith('/'):
base_url = '/' + base_url
- if area_name and area_name.endswith('/'):
- area_name = area_name[:-1]
- if area_name:
- base_response_dct['area_name_slash'] = area_name + "/"
+ if map_name and map_name.endswith('/'):
+ map_name = map_name[:-1]
+ if map_name:
+ base_response_dct['map_name_slash'] = map_name + "/"
if base_url[-1] != '/':
base_url += '/'
- base_url += area_name + '/'
+ base_url += map_name + '/'
base_response_dct['extra_url'] = base_url
- area = None
- if area_name:
+ map = None
+ if map_name:
try:
- area = Area.objects.get(urn=area_name, available=True)
+ map = Map.objects.get(urn=map_name, available=True)
except ObjectDoesNotExist:
return None, redirect(reverse('chimere:index'))
else:
try:
- area = Area.objects.get(default=True)
- area_name = area.urn
+ map = Map.objects.get(default=True)
+ map_name = map.urn
except ObjectDoesNotExist:
pass
- base_response_dct['area'] = area
- base_response_dct['area_name'] = area_name
- if area and area.external_css:
- base_response_dct['css_area'] = area.external_css
+ base_response_dct['map'] = map
+ base_response_dct['map_name'] = map_name
+ if map and map.external_css:
+ base_response_dct['css_map'] = map.external_css
base_response_dct['dynamic_categories'] = True \
- if area and area.dynamic_categories else False
+ if map and map.dynamic_categories else False
base_response_dct['JQUERY_JS_URLS'] = settings.JQUERY_JS_URLS
base_response_dct['JQUERY_CSS_URLS'] = settings.JQUERY_CSS_URLS
base_response_dct['PROJECT_NAME'] = settings.PROJECT_NAME
return base_response_dct, None
-def getShareUrl(request, area_name='', network=''):
+def getShareUrl(request, map_name='', network=''):
"""
Get a share url
"""
- data = getTinyfiedUrl(request, request.GET.urlencode(), area_name)
+ data = getTinyfiedUrl(request, request.GET.urlencode(), map_name)
for name, url, img in settings.CHIMERE_SHARE_NETWORKS:
if defaultfilters.slugify(name) == network:
return HttpResponse(url % {'text':data['text'], 'url':data['url']})
return HttpResponse('')
-def getShareNetwork(request, area_name='', marker=None):
+def getShareNetwork(request, map_name='', marker=None):
"""
Get URLs to share items
"""
@@ -128,13 +128,13 @@ def getShareNetwork(request, area_name='', marker=None):
parameters = u'current_feature=%d' % marker.pk
parameters += u"&checked_categories=%s" % "_".join([str(m.id) \
for m in marker.categories.all()])
- net_dct = getTinyfiedUrl(request, parameters, area_name)
+ net_dct = getTinyfiedUrl(request, parameters, map_name)
share_networks = []
for network in settings.CHIMERE_SHARE_NETWORKS:
share_networks.append((network[0], network[1] % net_dct, network[2]))
return share_networks, net_dct
-def index(request, area_name=None, default_area=None, simple=False,
+def index(request, map_name=None, default_map=None, simple=False,
get_response=False):
"""
Main page
@@ -147,7 +147,7 @@ def index(request, area_name=None, default_area=None, simple=False,
request.session['last_visit'] != today:
request.session['last_visit'] = today
news_visible = True
- response_dct, redir = get_base_response(area_name)
+ response_dct, redir = get_base_response(map_name)
if redir:
return redir
# don't mess with permalink
@@ -167,21 +167,21 @@ def index(request, area_name=None, default_area=None, simple=False,
except:
pass
response_dct.update({
- 'actions':actions(response_dct['area_name']),
+ 'actions':actions(response_dct['map_name']),
'action_selected':('view',),
'error_message':'',
'news_visible': news_visible,
- 'areas_visible': settings.CHIMERE_DISPLAY_AREAS,
+ 'maps_visible': settings.CHIMERE_DISPLAY_MAPS,
'map_layer':settings.CHIMERE_DEFAULT_MAP_LAYER,
'dynamic_categories':response_dct['dynamic_categories'],
'zoomout':zoomout,
- 'has_default_area':Area.objects.filter(default=True).count(),
+ 'has_default_map':Map.objects.filter(default=True).count(),
'zoomout':zoomout
})
if hasattr(settings, 'CONTACT_EMAIL') and settings.CONTACT_EMAIL:
response_dct['contact_email'] = settings.CONTACT_EMAIL
response_dct['share_networks'], net_dct = \
- getShareNetwork(request, response_dct['area_name'])
+ getShareNetwork(request, response_dct['map_name'])
tpl = 'chimere/main_map.html'
if simple:
tpl = 'chimere/main_map_simple.html'
@@ -196,13 +196,13 @@ def get_edit_page(redirect_url, item_cls, item_form,
"""
Edition page
"""
- def func(request, area_name="", item_id=None, cat_type=['M']):
- response_dct, redir = get_base_response(area_name)
+ def func(request, map_name="", item_id=None, cat_type=['M']):
+ response_dct, redir = get_base_response(map_name)
if redir:
return redir, None, None
- if 'area_name' in response_dct:
- area_name = response_dct['area_name']
- subcategories = SubCategory.getAvailable(cat_type, area_name,
+ if 'map_name' in response_dct:
+ map_name = response_dct['map_name']
+ subcategories = SubCategory.getAvailable(cat_type, map_name,
public=True)
listed_subcats = []
if subcategories:
@@ -215,7 +215,7 @@ def get_edit_page(redirect_url, item_cls, item_form,
try:
init_item = item_cls.objects.get(pk=item_id)
except:
- return redirect(redirect_url, area_name + '/' if area_name \
+ return redirect(redirect_url, map_name + '/' if map_name \
else ''), None, None
ref_item = init_item
modified_item = item_cls.objects.filter(ref_item=init_item,
@@ -284,9 +284,9 @@ def get_edit_page(redirect_url, item_cls, item_form,
f.save(marker)
base_uri = get_base_uri(request)
notifySubmission(base_uri, item)
- response_dct = get_base_response(area_name)
+ response_dct = get_base_response(map_name)
return redirect(redirect_url + '-item',
- area_name + '/' if area_name else '',
+ map_name + '/' if map_name else '',
item.ref_item.pk, 'submited'), None, subcategories
else:
response_dct['error_message'] = _(u"There are missing field(s)"
@@ -303,11 +303,11 @@ def get_edit_page(redirect_url, item_cls, item_form,
get_edit_marker = get_edit_page('chimere:edit', Marker, MarkerForm)
-def edit(request, area_name="", item_id=None, submited=False):
+def edit(request, map_name="", item_id=None, submited=False):
"""
Edition page
"""
- response, values, sub_categories = get_edit_marker(request, area_name,
+ response, values, sub_categories = get_edit_marker(request, map_name,
item_id, ['M', 'B'])
if response:
return response
@@ -322,7 +322,7 @@ def edit(request, area_name="", item_id=None, submited=False):
if request.POST and request.POST.get('point'):
point_value = request.POST.get('point')
response_dct.update({
- 'actions':actions(response_dct['area_name']),
+ 'actions':actions(response_dct['map_name']),
'action_selected':('contribute', 'edit'),
'map_layer':settings.CHIMERE_DEFAULT_MAP_LAYER,
'form':form,
@@ -334,7 +334,7 @@ def edit(request, area_name="", item_id=None, submited=False):
'sub_categories':sub_categories,
'point_widget':PointChooserWidget().render('point',
point_value,
- area_name=response_dct['area_name']),
+ map_name=response_dct['map_name']),
'properties':declared_fields,
'filtered_properties':filtered_properties,
'submited':submited
@@ -345,8 +345,8 @@ def edit(request, area_name="", item_id=None, submited=False):
return render_to_response('chimere/edit.html', response_dct,
context_instance=RequestContext(request))
-def uploadFile(request, category_id='', area_name=''):
- response_dct, redir = get_base_response(area_name)
+def uploadFile(request, category_id='', map_name=''):
+ response_dct, redir = get_base_response(map_name)
if redir:
return redir
Form = FileForm if not category_id else FullFileForm
@@ -393,7 +393,7 @@ def uploadFile(request, category_id='', area_name=''):
return render_to_response('chimere/upload_file.html', response_dct,
context_instance=RequestContext(request))
-def processRouteFile(request, area_name='', file_id=None):
+def processRouteFile(request, map_name='', file_id=None):
if file_id:
try:
route_file = RouteFile.objects.get(pk=file_id)
@@ -411,11 +411,11 @@ def processRouteFile(request, area_name='', file_id=None):
get_edit_route = get_edit_page('chimere:editroute', Route, RouteForm)
-def editRoute(request, area_name="", item_id=None, submited=False):
+def editRoute(request, map_name="", item_id=None, submited=False):
"""
Route edition page
"""
- response, values, sub_categories = get_edit_route(request, area_name,
+ response, values, sub_categories = get_edit_route(request, map_name,
item_id, ['R', 'B'])
if response:
return response
@@ -430,7 +430,7 @@ def editRoute(request, area_name="", item_id=None, submited=False):
if request.POST and request.POST.get('route'):
route_value = request.POST.get('route')
response_dct.update({
- 'actions':actions(response_dct['area_name']),
+ 'actions':actions(response_dct['map_name']),
'action_selected':('contribute', 'edit-route'),
'error_message':'',
'map_layer':settings.CHIMERE_DEFAULT_MAP_LAYER,
@@ -441,7 +441,7 @@ def editRoute(request, area_name="", item_id=None, submited=False):
'extra_head':form.media,
'sub_categories':sub_categories,
'route_widget':RouteChooserWidget().render('route', route_value,
- area_name=response_dct['area_name'], routefile_id='',),
+ map_name=response_dct['map_name'], routefile_id='',),
'properties':declared_fields,
'submited':submited
})
@@ -451,31 +451,31 @@ def editRoute(request, area_name="", item_id=None, submited=False):
return render_to_response('chimere/edit_route.html', response_dct,
context_instance=RequestContext(request))
-def submited(request, area_name="", action=""):
+def submited(request, map_name="", action=""):
"""
Successful submission page
"""
- response_dct, redir = get_base_response(area_name)
+ response_dct, redir = get_base_response(map_name)
if redir:
return redir
- response_dct.update({'actions':actions(response_dct['area_name']),
+ response_dct.update({'actions':actions(response_dct['map_name']),
'action_selected':action,})
return render_to_response('chimere/submited.html', response_dct,
context_instance=RequestContext(request))
-def charte(request, area_name=""):
+def charte(request, map_name=""):
"""
Affichage de la charte
"""
- response_dct, redir = get_base_response(area_name)
+ response_dct, redir = get_base_response(map_name)
if redir:
return redir
- response_dct.update({'actions':actions(response_dct['area_name']),
+ response_dct.update({'actions':actions(response_dct['map_name']),
'action_selected':('charte',)})
return render_to_response('chimere/charte.html', response_dct,
context_instance=RequestContext(request))
-def contactus(request, area_name=""):
+def contactus(request, map_name=""):
"""
Contact page
"""
@@ -496,16 +496,16 @@ def contactus(request, area_name=""):
msg = _(u"Temporary error. Renew your message later.")
else:
form = ContactForm()
- response_dct, redir = get_base_response(area_name)
+ response_dct, redir = get_base_response(map_name)
if redir:
return redir
- response_dct.update({'actions':actions(response_dct['area_name']),
+ response_dct.update({'actions':actions(response_dct['map_name']),
'action_selected':('contact',),
'contact_form':form, 'message':msg})
return render_to_response('chimere/contactus.html', response_dct,
context_instance=RequestContext(request))
-def extraPage(request, area_name="", page_id=""):
+def extraPage(request, map_name="", page_id=""):
"""
Extra dynamic pages
"""
@@ -513,10 +513,10 @@ def extraPage(request, area_name="", page_id=""):
page = Page.objects.get(available=True, mnemonic=page_id)
except ObjectDoesNotExist:
return redirect(reverse('chimere:index'))
- response_dct, redir = get_base_response(area_name)
+ response_dct, redir = get_base_response(map_name)
if redir:
return redir
- response_dct.update({'actions':actions(response_dct['area_name']),
+ response_dct.update({'actions':actions(response_dct['map_name']),
'action_selected':(page_id,),
'content':page.content,
'title':page.title})
@@ -525,7 +525,7 @@ def extraPage(request, area_name="", page_id=""):
return render_to_response(tpl, response_dct,
context_instance=RequestContext(request))
-def getDetail(request, area_name, marker_id):
+def getDetail(request, map_name, marker_id):
'''
Get the detail for a marker
'''
@@ -534,7 +534,7 @@ def getDetail(request, area_name, marker_id):
status__in=['A', 'S'])[0]
except (ValueError, IndexError):
return HttpResponse('no results')
- response_dct, redir = get_base_response(area_name)
+ response_dct, redir = get_base_response(map_name)
if redir:
return redir
response_dct['marker'] = marker
@@ -542,7 +542,7 @@ def getDetail(request, area_name, marker_id):
if 'simple' in request.GET and request.GET['simple']:
response_dct['simple'] = True
response_dct['share_networks'], net_dct = \
- getShareNetwork(request, response_dct['area_name'], marker)
+ getShareNetwork(request, response_dct['map_name'], marker)
response_dct['share_url'] = net_dct['url']
net_dct['to'] = settings.CONTACT_EMAIL
if net_dct['to']:
@@ -556,7 +556,7 @@ def getDetail(request, area_name, marker_id):
return render_to_response('chimere/detail.html', response_dct,
context_instance=RequestContext(request))
-def getDescriptionDetail(request, area_name, category_id):
+def getDescriptionDetail(request, map_name, category_id):
'''
Get the description for a category
'''
@@ -564,7 +564,7 @@ def getDescriptionDetail(request, area_name, category_id):
category = Category.objects.filter(id=int(category_id))[0]
except (ValueError, IndexError):
return HttpResponse('no results')
- response_dct, redir = get_base_response(area_name)
+ response_dct, redir = get_base_response(map_name)
if redir:
return redir
response_dct['category'] = category
@@ -586,7 +586,7 @@ def checkDate(q):
)
return q
-def getGeoObjects(request, area_name, category_ids, status):
+def getGeoObjects(request, map_name, category_ids, status):
'''
Get the JSON for markers and routes
'''
@@ -646,16 +646,16 @@ def getGeoObjects(request, area_name, category_ids, status):
return HttpResponse(data)
-def get_all_categories(request, area_name=None):
+def get_all_categories(request, map_name=None):
'''
Get all available categories in JSON
'''
- context_data, redir = get_base_response(area_name)
- area = context_data["area"]
+ context_data, redir = get_base_response(map_name)
+ map = context_data["map"]
subcategories = []
- if area:
- subcategories = list(area.getCategories('A',
- area_name=context_data['area_name']))
+ if map:
+ subcategories = list(map.getCategories('A',
+ map_name=context_data['map_name']))
else:
categories = SubCategory.getAvailable()
for cat, subcats in categories:
@@ -664,25 +664,25 @@ def get_all_categories(request, area_name=None):
jsons = json.dumps({'categories':subcats})
return HttpResponse(jsons)
-def get_available_categories(request, area_name=None, area=None, status='A',
+def get_available_categories(request, map_name=None, map=None, status='A',
force=None):
'''
- Get category menu for a designed area
+ Get category menu for a designed map
'''
- context_data, redir = get_base_response(area_name)
- area = context_data["area"]
+ context_data, redir = get_base_response(map_name)
+ map = context_data["map"]
if redir:
return redir
- if area and area.dynamic_categories and \
+ if map and map.dynamic_categories and \
not "current_extent" in request.GET:
context_data['sub_categories'] = []
return render_to_response('chimere/blocks/categories.html', context_data,
context_instance=RequestContext(request))
- if not area or not area.dynamic_categories:
+ if not map or not map.dynamic_categories:
# Categories are not updated dynamicaly when the user move the map
# so we return ALL the categories
subcategories = SubCategory.getAvailable(
- area_name=context_data['area_name'])
+ map_name=context_data['map_name'])
context_data['sub_categories'] = subcategories
return render_to_response('chimere/blocks/categories.html', context_data,
context_instance=RequestContext(request))
@@ -694,12 +694,12 @@ def get_available_categories(request, area_name=None, area=None, status='A',
status = status.split('_')
current_extent = request.GET["current_extent"].replace('M', '-')\
.replace('D', '.')
- area = SimpleArea([float(pt) for pt in current_extent.split('_')])
+ map = SimpleArea([float(pt) for pt in current_extent.split('_')])
except:
# bad extent format
return HttpResponse(default_message)
- # if not force and area.isIn(SimpleArea(cookie.AREA):return
- categories = area.getCategories(status, area_name=context_data['area_name'])
+ # if not force and map.isIn(SimpleArea(cookie.AREA):return
+ categories = map.getCategories(status, map_name=context_data['map_name'])
if not categories:
return HttpResponse(default_message)
get_cat = lambda subcat: subcat.category
@@ -712,7 +712,7 @@ def get_available_categories(request, area_name=None, area=None, status='A',
return render_to_response('chimere/blocks/categories.html', context_data,
context_instance=RequestContext(request))
-def getCategory(request, area_name='', category_id=0):
+def getCategory(request, map_name='', category_id=0):
'''
Get the JSON for a category (mainly in order to get the description)
'''
@@ -722,7 +722,7 @@ def getCategory(request, area_name='', category_id=0):
return HttpResponse('no results')
return HttpResponse(category.getJSON())
-def getTinyfiedUrl(request, parameters, area_name=''):
+def getTinyfiedUrl(request, parameters, map_name=''):
'''
Get the tinyfied version of parameters
'''
@@ -731,11 +731,11 @@ def getTinyfiedUrl(request, parameters, area_name=''):
urn = TinyUrl.getUrnByParameters(parameters)
except:
return {}
- response_dct, redir = get_base_response(area_name)
+ response_dct, redir = get_base_response(map_name)
if redir:
return redir
- url = reverse('chimere:tiny', args=[(response_dct['area_name'] \
- if response_dct['area_name'] else '') + '/', urn])
+ url = reverse('chimere:tiny', args=[(response_dct['map_name'] \
+ if response_dct['map_name'] else '') + '/', urn])
if not url.startswith('http'):
url = get_base_uri(request) + url
url = re.sub("([^:])\/\/", "\g<1>/", url)
@@ -752,17 +752,17 @@ def getTinyfiedUrl(request, parameters, area_name=''):
data["text"] = urlquote(text)
return data
-def redirectFromTinyURN(request, area_name='', tiny_urn=''):
+def redirectFromTinyURN(request, map_name='', tiny_urn=''):
"""
Redirect from a tiny Urn
"""
parameters = '?' + TinyUrl.getParametersByUrn(tiny_urn)
- response_dct, redir = get_base_response(area_name)
+ response_dct, redir = get_base_response(map_name)
if redir:
return redir
return HttpResponseRedirect(response_dct['extra_url'] + parameters)
-def route(request, area_name, lon1, lat1, lonlat_steps, lon2, lat2,
+def route(request, map_name, lon1, lat1, lonlat_steps, lon2, lat2,
transport='foot', speed=''):
'''
Get the JSON for a route
@@ -828,18 +828,18 @@ def route(request, area_name, lon1, lat1, lonlat_steps, lon2, lat2,
message)
return HttpResponse(data)
-def get_json(request, area_name='', app_name='', filename=''):
+def get_json(request, map_name='', app_name='', filename=''):
return HttpResponse(open(settings.STATIC_ROOT+app_name+'/json/'+filename+'.json'),
'application/javascript', status=200)
-def rss(request, area_name=''):
+def rss(request, map_name=''):
'''
Redirect to RSS subscription page
'''
- response_dct, redir = get_base_response(area_name)
+ response_dct, redir = get_base_response(map_name)
if redir:
return redir
- response_dct.update({'actions':actions(response_dct['area_name']),
+ response_dct.update({'actions':actions(response_dct['map_name']),
'action_selected':('rss',),
'category_rss_feed':'',})
# If the form has been submited
@@ -861,14 +861,14 @@ def rss(request, area_name=''):
# User wants to follow all the new POI situated in a defined area
elif request.POST['rss_category'] == 'area':
# An unbound form
- form = AreaForm()
+ form = MapForm()
area_widget = AreaWidget().render('area', None)
response_dct.update({
'map_layer':settings.CHIMERE_DEFAULT_MAP_LAYER,
'extra_head':form.media,
'form':form,
'category_rss_feed':'area',
- 'area_id':Area.getAvailable(),
+ 'map_id':Map.getAvailable(),
'area_widget':area_widget
})
return render_to_response('chimere/feeds/rss.html',
@@ -899,10 +899,10 @@ def rss(request, area_name=''):
kwargs={'category_id':cat_id})
return redirect(feeds_link)
- # User has specified the ID of the area he wants to follow
- if 'id_area' in request.POST and request.POST['id_area'] != '':
- feeds_link = reverse('chimere:feeds-areaid',
- kwargs={'area_id':request.POST['id_area']})
+ # User has specified the ID of the map he wants to follow
+ if 'id_map' in request.POST and request.POST['id_map'] != '':
+ feeds_link = reverse('chimere:feeds-mapid',
+ kwargs={'map_id':request.POST['id_map']})
return redirect(feeds_link)
# User has specified the area he wants to follow => we redirect him
@@ -937,12 +937,12 @@ def rss(request, area_name=''):
context_instance=RequestContext(request))
if request.GET['rss_category'] == 'area':
# An unbound form
- form = AreaForm()
+ form = MapForm()
response_dct.update({'map_layer':settings.MAP_LAYER,
'extra_head':form.media,
'form':form,
'category_rss_feed':'area',
- 'area_id':Area.getAvailable(),
+ 'map_id':Map.getAvailable(),
'area_widget':AreaWidget().render('area', None)})
return render_to_response('chimere/feeds/rss.html', response_dct,
context_instance=RequestContext(request))
diff --git a/chimere/widgets.py b/chimere/widgets.py
index cbc3e19..8416690 100644
--- a/chimere/widgets.py
+++ b/chimere/widgets.py
@@ -36,7 +36,7 @@ from django.template.loader import render_to_string
import re
-def getMapJS(area_name=''):
+def getMapJS(map_name=''):
'''Variable initialization for drawing the map
'''
# projection, center and bounds definitions
@@ -53,30 +53,30 @@ def getMapJS(area_name=''):
js += u"var map_layer = %s;\n" % settings.CHIMERE_DEFAULT_MAP_LAYER
js += u"var restricted_extent;\n"
- if area_name:
- js += u"var area_name='%s';\n" % area_name
+ if map_name:
+ js += u"var map_name='%s';\n" % map_name
js = u"\n" % js
return js
-def get_map_layers(area_name=''):
- from chimere.models import Area
- area = None
- if area_name:
+def get_map_layers(map_name=''):
+ from chimere.models import Map
+ map = None
+ if map_name:
try:
- area = Area.objects.get(urn=area_name)
+ map = Map.objects.get(urn=map_name)
except ObjectDoesNotExist:
pass
else:
try:
- area = Area.objects.get(default=True)
+ map = Map.objects.get(default=True)
except ObjectDoesNotExist:
pass
map_layers, default = [], None
- if area and area.layers.count():
+ if map and map.layers.count():
map_layers = [[layer.name, layer.layer_code, False]
- for layer in area.layers.order_by('arealayers__order').all()]
- def_layer = area.layers.filter(arealayers__default=True)
+ for layer in map.layers.order_by('maplayers__order').all()]
+ def_layer = map.layers.filter(maplayers__default=True)
if def_layer.count():
def_layer = def_layer.all()[0]
for order, map_layer in enumerate(map_layers):
@@ -225,7 +225,7 @@ class DatePickerWidget(forms.TextInput):
class NominatimWidget(forms.TextInput):
class Media:
js = ["%schimere/js/nominatim-widget.js" % settings.STATIC_URL]
- def render(self, name, value, attrs=None, area_name=''):
+ def render(self, name, value, attrs=None, map_name=''):
tpl = u"""
@@ -255,7 +255,7 @@ class PointChooserWidget(forms.TextInput):
js = settings.MAP_JS_URLS[settings.CHIMERE_EDIT_RENDERER] + \
list(settings.JQUERY_JS_URLS)
- def render(self, name, value, attrs=None, area_name=''):
+ def render(self, name, value, attrs=None, map_name=''):
'''
Render a map and latitude, longitude information field
'''
@@ -273,9 +273,9 @@ class PointChooserWidget(forms.TextInput):
value = None
else:
value = None
- map_layers, default_area = get_map_layers(area_name)
+ map_layers, default_map = get_map_layers(map_name)
map_layers = [js for n, js, default in map_layers]
- #TODO: manage area
+ #TODO: manage maps
return mark_safe(
render_to_string('chimere/blocks/live_coordinates.html',
{'lat': _("Latitude"),
@@ -285,7 +285,7 @@ class PointChooserWidget(forms.TextInput):
'name': name,
'val': val,
'isvalue': bool(value),
- 'default_area': "true" if default_area else "false",
+ 'default_map': "true" if default_map else "false",
}) % \
(settings.STATIC_URL,
settings.CHIMERE_EPSG_DISPLAY_PROJECTION,
@@ -325,12 +325,12 @@ class RouteChooserWidget(forms.TextInput):
["%schimere/js/edit_route_map.js" % settings.STATIC_URL,
"%schimere/js/base.js" % settings.STATIC_URL,]
- def render(self, name, value, attrs=None, area_name='', routefile_id=None):
+ def render(self, name, value, attrs=None, map_name='', routefile_id=None):
'''
Render a map and latitude, longitude information field
'''
- tpl = getMapJS(area_name)
- map_layers, default_area = get_map_layers(area_name)
+ tpl = getMapJS(map_name)
+ map_layers, default_map = get_map_layers(map_name)
map_layers = [js for nm, js, default in map_layers]
js = """
var resolutions;
@@ -352,12 +352,12 @@ class RouteChooserWidget(forms.TextInput):
settings.CHIMERE_EPSG_DISPLAY_PROJECTION,
settings.CHIMERE_EPSG_PROJECTION, settings.CHIMERE_DEFAULT_CENTER,
settings.CHIMERE_DEFAULT_ZOOM, ", ".join(map_layers))
- if default_area:
+ if default_map:
js += "chimere_init_options['selected_map_layer'] = %d;\n" % \
- default_area
+ default_map
tpl = u"\n" % js
- #TODO: manage area
+ #TODO: manage maps
help_create = ''
if not value:
help_create = u"%s \n"\
@@ -450,7 +450,7 @@ class RouteField(models.LineStringField):
class AreaWidget(forms.TextInput):
"""
- Manage the edition of an area on the map
+ Manage the edition of an areaon the map
"""
class Media:
css = {
--
cgit v1.2.3
From 81cc55bdada577a8b3c3cf1304f48c64810083aa Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Sun, 16 Jun 2013 21:02:24 +0200
Subject: Display/write rights management on maps
---
chimere/admin.py | 8 +-
chimere/forms.py | 2 +-
...eld_map_public_read__add_field_map_public_pr.py | 359 +++++++++++++++++++++
chimere/models.py | 16 +-
chimere/src/default-bg.xcf | Bin 0 -> 3214457 bytes
chimere/src/default_bg.xcf | Bin 0 -> 3231304 bytes
chimere/static/chimere/css/styles.css | 24 ++
chimere/static/chimere/img/default-bg.jpg | Bin 0 -> 82181 bytes
chimere/templates/chimere/main_map.html | 16 +-
chimere/templates/chimere/no_map.html | 40 +++
chimere/urls.py | 1 +
chimere/views.py | 102 ++++--
12 files changed, 536 insertions(+), 32 deletions(-)
create mode 100644 chimere/migrations/0006_auto__add_mapusers__add_field_map_public_read__add_field_map_public_pr.py
create mode 100644 chimere/src/default-bg.xcf
create mode 100644 chimere/src/default_bg.xcf
create mode 100644 chimere/static/chimere/img/default-bg.jpg
create mode 100644 chimere/templates/chimere/no_map.html
diff --git a/chimere/admin.py b/chimere/admin.py
index f30c00b..67e0e51 100644
--- a/chimere/admin.py
+++ b/chimere/admin.py
@@ -42,7 +42,7 @@ from chimere.forms import MarkerAdminForm, RouteAdminForm, MapAdminForm,\
from chimere.models import Category, Icon, SubCategory, Marker, \
PropertyModel, News, Route, Map, ColorTheme, Color, \
MultimediaFile, PictureFile, Importer, Layer, MapLayers,\
- PropertyModelChoice, MultimediaExtension, Page,\
+ PropertyModelChoice, MultimediaExtension, Page, MapUsers,\
get_maps_for_user, get_users_by_map
from chimere.utils import unicode_normalize, ShapefileManager, KMLManager,\
CSVManager
@@ -317,13 +317,17 @@ class LayerInline(admin.TabularInline):
model = MapLayers
extra = 1
+class UserInline(admin.TabularInline):
+ model = MapUsers
+ extra = 1
+
class MapAdmin(admin.ModelAdmin):
"""
Specialized the map field.
"""
form = MapAdminForm
exclude = ['upper_left_corner', 'lower_right_corner']
- inlines = [LayerInline]
+ inlines = [UserInline, LayerInline]
list_display = ['name', 'order', 'available', 'default']
def importing(modeladmin, request, queryset):
diff --git a/chimere/forms.py b/chimere/forms.py
index b6d5feb..09b46ae 100644
--- a/chimere/forms.py
+++ b/chimere/forms.py
@@ -580,7 +580,7 @@ class MapAdminForm(forms.ModelForm):
new_area.lower_right_corner = 'POINT(%s %s)' % (area[1][0],
area[1][1])
content_type = ContentType.objects.get(app_label="chimere",
- model="area")
+ model="map")
if new_area.urn:
mnemo = 'change_map_' + new_area.urn
perm = Permission.objects.filter(codename=mnemo)
diff --git a/chimere/migrations/0006_auto__add_mapusers__add_field_map_public_read__add_field_map_public_pr.py b/chimere/migrations/0006_auto__add_mapusers__add_field_map_public_read__add_field_map_public_pr.py
new file mode 100644
index 0000000..65f9281
--- /dev/null
+++ b/chimere/migrations/0006_auto__add_mapusers__add_field_map_public_read__add_field_map_public_pr.py
@@ -0,0 +1,359 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding model 'MapUsers'
+ db.create_table('chimere_mapusers', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('map', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['chimere.Map'])),
+ ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
+ ('read', self.gf('django.db.models.fields.BooleanField')(default=False)),
+ ('propose', self.gf('django.db.models.fields.BooleanField')(default=False)),
+ ('write', self.gf('django.db.models.fields.BooleanField')(default=False)),
+ ))
+ db.send_create_signal('chimere', ['MapUsers'])
+
+ # Adding field 'Map.public_read'
+ db.add_column('chimere_map', 'public_read',
+ self.gf('django.db.models.fields.BooleanField')(default=False),
+ keep_default=False)
+
+ # Adding field 'Map.public_propose'
+ db.add_column('chimere_map', 'public_propose',
+ self.gf('django.db.models.fields.BooleanField')(default=False),
+ keep_default=False)
+
+ # Adding field 'Map.public_write'
+ db.add_column('chimere_map', 'public_write',
+ self.gf('django.db.models.fields.BooleanField')(default=False),
+ keep_default=False)
+
+
+ def backwards(self, orm):
+ # Deleting model 'MapUsers'
+ db.delete_table('chimere_mapusers')
+
+ # Deleting field 'Map.public_read'
+ db.delete_column('chimere_map', 'public_read')
+
+ # Deleting field 'Map.public_propose'
+ db.delete_column('chimere_map', 'public_propose')
+
+ # Deleting field 'Map.public_write'
+ db.delete_column('chimere_map', 'public_write')
+
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'chimere.aggregatedroute': {
+ 'Meta': {'object_name': 'AggregatedRoute', 'db_table': "'chimere_aggregated_routes'", 'managed': 'False'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'route': ('django.contrib.gis.db.models.fields.MultiLineStringField', [], {}),
+ 'status': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'subcategory': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.SubCategory']"})
+ },
+ 'chimere.category': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'Category'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.color': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'Color'},
+ 'code': ('django.db.models.fields.CharField', [], {'max_length': '6'}),
+ 'color_theme': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'colors'", 'to': "orm['chimere.ColorTheme']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.colortheme': {
+ 'Meta': {'object_name': 'ColorTheme'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.icon': {
+ 'Meta': {'object_name': 'Icon'},
+ 'height': ('django.db.models.fields.IntegerField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'width': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.importer': {
+ 'Meta': {'object_name': 'Importer'},
+ 'associate_marker_to_way': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'categories': ('chimere.widgets.SelectMultipleField', [], {'to': "orm['chimere.SubCategory']", 'symmetrical': 'False'}),
+ 'default_description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'default_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'filtr': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'get_description': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'importer_type': ('django.db.models.fields.CharField', [], {'max_length': '4'}),
+ 'license': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'origin': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'overwrite': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'source': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'source_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'srid': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'state': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'zipped': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'chimere.layer': {
+ 'Meta': {'object_name': 'Layer'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layer_code': ('django.db.models.fields.TextField', [], {'max_length': '300'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.map': {
+ 'Meta': {'ordering': "('order', 'name')", 'object_name': 'Map'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'default': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'default_subcategories': ('chimere.widgets.SelectMultipleField', [], {'to': "orm['chimere.SubCategory']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'dynamic_categories': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'external_css': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layers': ('chimere.widgets.SelectMultipleField', [], {'symmetrical': 'False', 'related_name': "'maps'", 'blank': 'True', 'through': "orm['chimere.MapLayers']", 'to': "orm['chimere.Layer']"}),
+ 'lower_right_corner': ('django.contrib.gis.db.models.fields.PointField', [], {'default': "'POINT(0 0)'"}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'unique': 'True'}),
+ 'public_propose': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'public_read': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'public_write': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'restrict_to_extent': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'subcategories': ('chimere.widgets.SelectMultipleField', [], {'symmetrical': 'False', 'related_name': "'maps'", 'blank': 'True', 'db_table': "'chimere_subcategory_maps'", 'to': "orm['chimere.SubCategory']"}),
+ 'upper_left_corner': ('django.contrib.gis.db.models.fields.PointField', [], {'default': "'POINT(0 0)'"}),
+ 'urn': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'blank': 'True'}),
+ 'users': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'through': "orm['chimere.MapUsers']", 'symmetrical': 'False'}),
+ 'welcome_message': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.maplayers': {
+ 'Meta': {'ordering': "('order',)", 'object_name': 'MapLayers'},
+ 'default': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.Layer']"}),
+ 'map': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.Map']"}),
+ 'order': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.mapusers': {
+ 'Meta': {'object_name': 'MapUsers'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'map': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.Map']"}),
+ 'propose': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'read': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+ 'write': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'chimere.marker': {
+ 'Meta': {'ordering': "('status', 'name')", 'object_name': 'Marker'},
+ 'available_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'categories': ('chimere.widgets.SelectMultipleField', [], {'to': "orm['chimere.SubCategory']", 'symmetrical': 'False'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'end_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'import_key': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'import_source': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'import_version': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'is_front_page': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'license': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'modified_since_import': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'not_for_osm': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'origin': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'point': ('chimere.widgets.PointField', [], {}),
+ 'ref_item': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'submited_marker'", 'null': 'True', 'to': "orm['chimere.Marker']"}),
+ 'route': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'associated_marker'", 'null': 'True', 'to': "orm['chimere.Route']"}),
+ 'start_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'status': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'submiter_comment': ('django.db.models.fields.TextField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'submiter_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
+ 'submiter_name': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
+ 'submiter_session_key': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
+ 'weight': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.multimediaextension': {
+ 'Meta': {'object_name': 'MultimediaExtension'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'multimedia_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'extensions'", 'to': "orm['chimere.MultimediaType']"}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '6'})
+ },
+ 'chimere.multimediafile': {
+ 'Meta': {'object_name': 'MultimediaFile'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'marker': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'multimedia_files'", 'to': "orm['chimere.Marker']"}),
+ 'miniature': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'multimedia_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.MultimediaType']", 'null': 'True', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'url': ('django.db.models.fields.URLField', [], {'max_length': '200'})
+ },
+ 'chimere.multimediatype': {
+ 'Meta': {'object_name': 'MultimediaType'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'iframe': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'media_type': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'mime_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.news': {
+ 'Meta': {'object_name': 'News'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'content': ('django.db.models.fields.TextField', [], {}),
+ 'date': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_front_page': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'maps': ('chimere.widgets.SelectMultipleField', [], {'symmetrical': 'False', 'to': "orm['chimere.Map']", 'null': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.page': {
+ 'Meta': {'object_name': 'Page'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'content': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mnemonic': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '10', 'null': 'True', 'blank': 'True'}),
+ 'template_path': ('django.db.models.fields.CharField', [], {'max_length': '150', 'null': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.picturefile': {
+ 'Meta': {'object_name': 'PictureFile'},
+ 'height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'marker': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'pictures'", 'to': "orm['chimere.Marker']"}),
+ 'miniature': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'picture': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
+ 'thumbnailfile': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'thumbnailfile_height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'thumbnailfile_width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.property': {
+ 'Meta': {'object_name': 'Property'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'marker': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.Marker']"}),
+ 'propertymodel': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.PropertyModel']"}),
+ 'value': ('django.db.models.fields.TextField', [], {})
+ },
+ 'chimere.propertymodel': {
+ 'Meta': {'ordering': "('order',)", 'object_name': 'PropertyModel'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mandatory': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {}),
+ 'subcategories': ('chimere.widgets.SelectMultipleField', [], {'symmetrical': 'False', 'related_name': "'properties'", 'blank': 'True', 'to': "orm['chimere.SubCategory']"}),
+ 'type': ('django.db.models.fields.CharField', [], {'max_length': '1'})
+ },
+ 'chimere.propertymodelchoice': {
+ 'Meta': {'object_name': 'PropertyModelChoice'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'propertymodel': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'choices'", 'to': "orm['chimere.PropertyModel']"}),
+ 'value': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.route': {
+ 'Meta': {'ordering': "('status', 'name')", 'object_name': 'Route'},
+ 'associated_file': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.RouteFile']", 'null': 'True', 'blank': 'True'}),
+ 'categories': ('chimere.widgets.SelectMultipleField', [], {'to': "orm['chimere.SubCategory']", 'symmetrical': 'False'}),
+ 'end_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'has_associated_marker': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'import_key': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'import_source': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'import_version': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'license': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'modified_since_import': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'not_for_osm': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'origin': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'picture': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'ref_item': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'submited_route'", 'null': 'True', 'to': "orm['chimere.Route']"}),
+ 'route': ('chimere.widgets.RouteField', [], {}),
+ 'start_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'status': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'submiter_comment': ('django.db.models.fields.TextField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'submiter_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
+ 'submiter_name': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
+ 'submiter_session_key': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
+ 'width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.routefile': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'RouteFile'},
+ 'file_type': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'raw_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+ 'simplified_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.subcategory': {
+ 'Meta': {'ordering': "['category', 'order']", 'object_name': 'SubCategory'},
+ 'as_layer': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'category': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'subcategories'", 'to': "orm['chimere.Category']"}),
+ 'color_theme': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.ColorTheme']", 'null': 'True', 'blank': 'True'}),
+ 'dated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'hover_icon': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'subcat_hovered'", 'null': 'True', 'to': "orm['chimere.Icon']"}),
+ 'icon': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.Icon']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'item_type': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '1000'}),
+ 'routing_warn': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'submission': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'weighted': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'chimere.tinyurl': {
+ 'Meta': {'object_name': 'TinyUrl'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'parameters': ('django.db.models.fields.CharField', [], {'max_length': '500'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['chimere']
\ No newline at end of file
diff --git a/chimere/models.py b/chimere/models.py
index 60e4cd1..a1d96d0 100644
--- a/chimere/models.py
+++ b/chimere/models.py
@@ -1353,12 +1353,13 @@ class Map(models.Model, SimpleArea):
"""A map
"""
name = models.CharField(_(u"Name"), max_length=150)
+ available = models.BooleanField(_(u"Available"))
+ users = models.ManyToManyField(User, through='MapUsers')
urn = models.SlugField(_(u"Map urn"), max_length=50, blank=True,
unique=True)
welcome_message = models.TextField(_(u"Welcome message"), blank=True,
null=True)
order = models.IntegerField(_(u"Order"), unique=True)
- available = models.BooleanField(_(u"Available"))
upper_left_corner = models.PointField(_(u"Upper left corner"),
default='POINT(0 0)', srid=settings.CHIMERE_EPSG_DISPLAY_PROJECTION)
lower_right_corner = models.PointField(_(u"Lower right corner"),
@@ -1380,6 +1381,9 @@ class Map(models.Model, SimpleArea):
u"available"))
external_css = models.URLField(_(u"Link to an external CSS"), blank=True,
null=True)
+ public_read = models.BooleanField(_(u"Public can read the map"))
+ public_propose = models.BooleanField(_(u"Public can propose item to the map"))
+ public_write = models.BooleanField(_(u"Public can write without moderation to the map"))
restrict_to_extent = models.BooleanField(_(u"Restrict to the area extent"),
default=False)
objects = models.GeoManager()
@@ -1522,6 +1526,16 @@ def get_users_by_map(map):
return User.objects.filter(Q(groups__permissions__codename=perm)|
Q(user_permissions__codename=perm)).all()
+class MapUsers(models.Model):
+ map = models.ForeignKey(Map, related_name='mapusers')
+ user = models.ForeignKey(User, related_name='mapusers')
+ read = models.BooleanField(_(u"Can read the map"))
+ propose = models.BooleanField(_(u"Can propose item to the map"))
+ write = models.BooleanField(_(u"Can write without moderation to the map"))
+ class Meta:
+ verbose_name = _("Map - user")
+ verbose_name_plural = _("Map - users")
+
class MapLayers(models.Model):
map = models.ForeignKey(Map)
layer = models.ForeignKey(Layer)
diff --git a/chimere/src/default-bg.xcf b/chimere/src/default-bg.xcf
new file mode 100644
index 0000000..71ad247
Binary files /dev/null and b/chimere/src/default-bg.xcf differ
diff --git a/chimere/src/default_bg.xcf b/chimere/src/default_bg.xcf
new file mode 100644
index 0000000..453bf15
Binary files /dev/null and b/chimere/src/default_bg.xcf differ
diff --git a/chimere/static/chimere/css/styles.css b/chimere/static/chimere/css/styles.css
index 6986189..c0844df 100644
--- a/chimere/static/chimere/css/styles.css
+++ b/chimere/static/chimere/css/styles.css
@@ -51,6 +51,7 @@ body, h2, h3, th,
background-image:None;
}
+#no-content .alert,
fieldset, .action li, #content,
#layer_selection #layer_list,
#map-footer, #panel, #chimere_itinerary_panel, #maps,
@@ -74,6 +75,7 @@ div.warning, .errorlist{
border:1px solid #54c200;
}
+#no-content .alert,
#layer_selection h4,
#layer_selection #layer_list,
#maps, #detail, #main-map,
@@ -234,6 +236,28 @@ ul#action-2 {
padding:20px;
}
+#no-content{
+ position:absolute;
+ bottom:0;
+ top:0;
+ right:0;
+ left:0;
+ background-image:url(../img/default-bg.jpg)
+}
+
+#no-content .alert{
+ width:400px;
+ padding:0.5em;
+ margin-left:auto;
+ margin-right:auto;
+ margin-top:25%;
+ padding:0.5em 1em;
+}
+
+#auth p{
+ text-align:center;
+}
+
#footer{
margin:5px;
text-align:center;
diff --git a/chimere/static/chimere/img/default-bg.jpg b/chimere/static/chimere/img/default-bg.jpg
new file mode 100644
index 0000000..a3e96f0
Binary files /dev/null and b/chimere/static/chimere/img/default-bg.jpg differ
diff --git a/chimere/templates/chimere/main_map.html b/chimere/templates/chimere/main_map.html
index 4187440..b6a0774 100644
--- a/chimere/templates/chimere/main_map.html
+++ b/chimere/templates/chimere/main_map.html
@@ -1,5 +1,6 @@
{% extends "chimere/base.html" %}
{% load i18n unlocalize_point chimere_tags %}
+{% load url from future %}
{% block extra_head %}
{% head_jquery %}
{% head_chimere %}
@@ -8,6 +9,19 @@
{% endblock %}
{% block message_edit %}{% endblock %}
{% block sidebar %}
+ {{block.super}}
+
+ {% if not is_authenticated %}
+
+ {% else %}
+
{% trans "Identified as: " %}{{user}} - {% trans "Logout"%}
+ {% endif %}
+
{% endblock %}
diff --git a/chimere/templates/chimere/no_map.html b/chimere/templates/chimere/no_map.html
new file mode 100644
index 0000000..c707a0b
--- /dev/null
+++ b/chimere/templates/chimere/no_map.html
@@ -0,0 +1,40 @@
+{% extends "chimere/base.html" %}
+{% load i18n unlocalize_point chimere_tags %}
+{% block extra_head %}
+{% head_jquery %}
+{% head_chimere %}
+{% head_jme %}
+{{ block.super }}
+{% endblock %}
+{% block sidebar %}
+ {{block.super}}
+
+ {% if not is_authenticated %}
+
+ {% else %}
+
{% trans "Identified as: " %}{{user}} - {% trans "Logout"%}
+ {% endif %}
+
+{% endblock %}
+{% block content %}
+ {{block.super}}
+
+
+
+ {% if is_authenticated %}
+ {% trans "No map are currently available for your account." %}
+ {% else %}
+ {% trans "No map are currently available for public access. If you have an account identify yourself." %}
+ {% endif %}
+
+
+
+{% endblock %}
+{% block footer %}
+
+{% endblock %}
diff --git a/chimere/urls.py b/chimere/urls.py
index 36275ab..f87522e 100644
--- a/chimere/urls.py
+++ b/chimere/urls.py
@@ -69,6 +69,7 @@ if hasattr(settings, 'CHIMERE_ENABLE_ROUTING') \
)
urlpatterns += patterns('chimere.views',
+ url(r'^logout/?$', 'logout_view', name="logout"),
url(r'^charte/?$', 'charte', name="charte"),
url(r'^(?P[a-zA-Z0-9_-]+/)?contact/?$', 'contactus',
name="contact"),
diff --git a/chimere/views.py b/chimere/views.py
index f48a3b1..18039b7 100644
--- a/chimere/views.py
+++ b/chimere/views.py
@@ -29,6 +29,8 @@ from itertools import groupby
import re
from django.conf import settings
+from django.contrib.auth import authenticate, login, logout
+from django.contrib.auth.forms import AuthenticationForm
from django.contrib.gis.geos import GEOSGeometry
from django.contrib.gis.gdal.error import OGRException
from django.contrib.gis.measure import D
@@ -69,11 +71,12 @@ def get_base_uri(request):
return base_uri
#TODO: convert to requestcontext
-def get_base_response(map_name=""):
+def get_base_response(request, map_name="", edit=False):
"""
Get the base url
"""
- base_response_dct = {'media_path':settings.MEDIA_URL,}
+ base_response_dct = {'media_path':settings.MEDIA_URL,
+ 'is_authenticated':request.user.is_authenticated()}
base_url = reverse("chimere:index")
if not base_url.startswith('/'):
base_url = '/' + base_url
@@ -85,18 +88,39 @@ def get_base_response(map_name=""):
base_url += '/'
base_url += map_name + '/'
base_response_dct['extra_url'] = base_url
- map = None
+ map_filter = {'available':True}
if map_name:
- try:
- map = Map.objects.get(urn=map_name, available=True)
- except ObjectDoesNotExist:
- return None, redirect(reverse('chimere:index'))
+ map_filter['urn'] = map_name
else:
- try:
- map = Map.objects.get(default=True)
- map_name = map.urn
- except ObjectDoesNotExist:
- pass
+ map_filter['default'] = True
+ filters = [{'public_write':True},
+ {'public_propose':True},
+ ] if edit else [
+ {'public_read':True}]
+ if base_response_dct['is_authenticated']:
+ filters += [{'mapusers__user':request.user, 'mapusers__write':True},
+ {'mapusers__user':request.user, 'mapusers__propose':True},
+ ] if edit else [
+ {'mapusers__user':request.user, 'mapusers__read':True},]
+ query = None
+ for fltr in filters:
+ fltr.update(map_filter)
+ if not query:
+ query = Q(**fltr)
+ else:
+ query = query | Q(**fltr)
+ map = None
+ try:
+ map = Map.objects.get(query)
+ map_name = map.urn
+ except ObjectDoesNotExist:
+ if map_name:
+ return None, redirect(reverse('chimere:index'))
+
+ if edit and map:
+ base_response_dct['can_write'] = bool(map.public_write or
+ Map.objects.filter(pk=map.pk, mapusers__user=request.user,
+ mapusers__write=True).count())
base_response_dct['map'] = map
base_response_dct['map_name'] = map_name
@@ -134,6 +158,11 @@ def getShareNetwork(request, map_name='', marker=None):
share_networks.append((network[0], network[1] % net_dct, network[2]))
return share_networks, net_dct
+
+def logout_view(request):
+ logout(request)
+ return redirect(reverse('chimere:index'))
+
def index(request, map_name=None, default_map=None, simple=False,
get_response=False):
"""
@@ -147,9 +176,28 @@ def index(request, map_name=None, default_map=None, simple=False,
request.session['last_visit'] != today:
request.session['last_visit'] = today
news_visible = True
- response_dct, redir = get_base_response(map_name)
+ response_dct, redir = get_base_response(request, map_name)
if redir:
return redir
+
+ if request.POST:
+ auth_form = AuthenticationForm(None, request.POST)
+ invalid_msg = _(u"Invalid user or password.")
+ if auth_form.is_valid():
+ user = authenticate(
+ username=auth_form.cleaned_data['username'],
+ password=auth_form.cleaned_data['password'])
+ if user is not None and user.is_active:
+ login(request, user)
+ return redirect(reverse('chimere:index'))
+ else:
+ response_dct['auth_form'] = auth_form
+ else:
+ response_dct['auth_form'] = AuthenticationForm()
+
+ if not response_dct['map']:
+ return render_to_response('chimere/no_map.html', response_dct,
+ context_instance=RequestContext(request))
# don't mess with permalink
zoomout = True
if request.GET and 'lat' in request.GET \
@@ -197,7 +245,7 @@ def get_edit_page(redirect_url, item_cls, item_form,
Edition page
"""
def func(request, map_name="", item_id=None, cat_type=['M']):
- response_dct, redir = get_base_response(map_name)
+ response_dct, redir = get_base_response(request, map_name)
if redir:
return redir, None, None
if 'map_name' in response_dct:
@@ -284,7 +332,7 @@ def get_edit_page(redirect_url, item_cls, item_form,
f.save(marker)
base_uri = get_base_uri(request)
notifySubmission(base_uri, item)
- response_dct = get_base_response(map_name)
+ response_dct = get_base_response(request, map_name)
return redirect(redirect_url + '-item',
map_name + '/' if map_name else '',
item.ref_item.pk, 'submited'), None, subcategories
@@ -346,7 +394,7 @@ def edit(request, map_name="", item_id=None, submited=False):
context_instance=RequestContext(request))
def uploadFile(request, category_id='', map_name=''):
- response_dct, redir = get_base_response(map_name)
+ response_dct, redir = get_base_response(request, map_name)
if redir:
return redir
Form = FileForm if not category_id else FullFileForm
@@ -455,7 +503,7 @@ def submited(request, map_name="", action=""):
"""
Successful submission page
"""
- response_dct, redir = get_base_response(map_name)
+ response_dct, redir = get_base_response(request, map_name)
if redir:
return redir
response_dct.update({'actions':actions(response_dct['map_name']),
@@ -467,7 +515,7 @@ def charte(request, map_name=""):
"""
Affichage de la charte
"""
- response_dct, redir = get_base_response(map_name)
+ response_dct, redir = get_base_response(request, map_name)
if redir:
return redir
response_dct.update({'actions':actions(response_dct['map_name']),
@@ -496,7 +544,7 @@ def contactus(request, map_name=""):
msg = _(u"Temporary error. Renew your message later.")
else:
form = ContactForm()
- response_dct, redir = get_base_response(map_name)
+ response_dct, redir = get_base_response(request, map_name)
if redir:
return redir
response_dct.update({'actions':actions(response_dct['map_name']),
@@ -513,7 +561,7 @@ def extraPage(request, map_name="", page_id=""):
page = Page.objects.get(available=True, mnemonic=page_id)
except ObjectDoesNotExist:
return redirect(reverse('chimere:index'))
- response_dct, redir = get_base_response(map_name)
+ response_dct, redir = get_base_response(request, map_name)
if redir:
return redir
response_dct.update({'actions':actions(response_dct['map_name']),
@@ -534,7 +582,7 @@ def getDetail(request, map_name, marker_id):
status__in=['A', 'S'])[0]
except (ValueError, IndexError):
return HttpResponse('no results')
- response_dct, redir = get_base_response(map_name)
+ response_dct, redir = get_base_response(request, map_name)
if redir:
return redir
response_dct['marker'] = marker
@@ -564,7 +612,7 @@ def getDescriptionDetail(request, map_name, category_id):
category = Category.objects.filter(id=int(category_id))[0]
except (ValueError, IndexError):
return HttpResponse('no results')
- response_dct, redir = get_base_response(map_name)
+ response_dct, redir = get_base_response(request, map_name)
if redir:
return redir
response_dct['category'] = category
@@ -650,7 +698,7 @@ def get_all_categories(request, map_name=None):
'''
Get all available categories in JSON
'''
- context_data, redir = get_base_response(map_name)
+ context_data, redir = get_base_response(request, map_name)
map = context_data["map"]
subcategories = []
if map:
@@ -669,7 +717,7 @@ def get_available_categories(request, map_name=None, map=None, status='A',
'''
Get category menu for a designed map
'''
- context_data, redir = get_base_response(map_name)
+ context_data, redir = get_base_response(request, map_name)
map = context_data["map"]
if redir:
return redir
@@ -731,7 +779,7 @@ def getTinyfiedUrl(request, parameters, map_name=''):
urn = TinyUrl.getUrnByParameters(parameters)
except:
return {}
- response_dct, redir = get_base_response(map_name)
+ response_dct, redir = get_base_response(request, map_name)
if redir:
return redir
url = reverse('chimere:tiny', args=[(response_dct['map_name'] \
@@ -757,7 +805,7 @@ def redirectFromTinyURN(request, map_name='', tiny_urn=''):
Redirect from a tiny Urn
"""
parameters = '?' + TinyUrl.getParametersByUrn(tiny_urn)
- response_dct, redir = get_base_response(map_name)
+ response_dct, redir = get_base_response(request, map_name)
if redir:
return redir
return HttpResponseRedirect(response_dct['extra_url'] + parameters)
@@ -836,7 +884,7 @@ def rss(request, map_name=''):
'''
Redirect to RSS subscription page
'''
- response_dct, redir = get_base_response(map_name)
+ response_dct, redir = get_base_response(request, map_name)
if redir:
return redir
response_dct.update({'actions':actions(response_dct['map_name']),
--
cgit v1.2.3
From 4417bdc2ed42a0910e4d0fcb3d42165a1cca9dd7 Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Tue, 18 Jun 2013 14:59:15 +0200
Subject: Enable map clustering option - fix category when there is only one
---
.../migrations/0007_auto__add_field_map_cluster.py | 330 +++++++++++++++++++++
chimere/models.py | 2 +
.../static/chimere/js/jquery.chimere-leaflet.js | 5 +-
chimere/templates/chimere/blocks/map.html | 1 +
chimere/templatetags/chimere_tags.py | 6 +-
5 files changed, 340 insertions(+), 4 deletions(-)
create mode 100644 chimere/migrations/0007_auto__add_field_map_cluster.py
diff --git a/chimere/migrations/0007_auto__add_field_map_cluster.py b/chimere/migrations/0007_auto__add_field_map_cluster.py
new file mode 100644
index 0000000..219487f
--- /dev/null
+++ b/chimere/migrations/0007_auto__add_field_map_cluster.py
@@ -0,0 +1,330 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding field 'Map.cluster'
+ db.add_column('chimere_map', 'cluster',
+ self.gf('django.db.models.fields.BooleanField')(default=False),
+ keep_default=False)
+
+
+ def backwards(self, orm):
+ # Deleting field 'Map.cluster'
+ db.delete_column('chimere_map', 'cluster')
+
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'chimere.aggregatedroute': {
+ 'Meta': {'object_name': 'AggregatedRoute', 'db_table': "'chimere_aggregated_routes'", 'managed': 'False'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'route': ('django.contrib.gis.db.models.fields.MultiLineStringField', [], {}),
+ 'status': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'subcategory': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.SubCategory']"})
+ },
+ 'chimere.category': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'Category'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.color': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'Color'},
+ 'code': ('django.db.models.fields.CharField', [], {'max_length': '6'}),
+ 'color_theme': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'colors'", 'to': "orm['chimere.ColorTheme']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.colortheme': {
+ 'Meta': {'object_name': 'ColorTheme'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.icon': {
+ 'Meta': {'object_name': 'Icon'},
+ 'height': ('django.db.models.fields.IntegerField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'width': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.importer': {
+ 'Meta': {'object_name': 'Importer'},
+ 'associate_marker_to_way': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'categories': ('chimere.widgets.SelectMultipleField', [], {'to': "orm['chimere.SubCategory']", 'symmetrical': 'False'}),
+ 'default_description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'default_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'filtr': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'get_description': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'importer_type': ('django.db.models.fields.CharField', [], {'max_length': '4'}),
+ 'license': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'origin': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'overwrite': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'source': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'source_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'srid': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'state': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'zipped': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'chimere.layer': {
+ 'Meta': {'object_name': 'Layer'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layer_code': ('django.db.models.fields.TextField', [], {'max_length': '300'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.map': {
+ 'Meta': {'ordering': "('order', 'name')", 'object_name': 'Map'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'cluster': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'default': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'default_subcategories': ('chimere.widgets.SelectMultipleField', [], {'to': "orm['chimere.SubCategory']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'dynamic_categories': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'external_css': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layers': ('chimere.widgets.SelectMultipleField', [], {'symmetrical': 'False', 'related_name': "'maps'", 'blank': 'True', 'through': "orm['chimere.MapLayers']", 'to': "orm['chimere.Layer']"}),
+ 'lower_right_corner': ('django.contrib.gis.db.models.fields.PointField', [], {'default': "'POINT(0 0)'"}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'unique': 'True'}),
+ 'public_propose': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'public_read': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'public_write': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'restrict_to_extent': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'subcategories': ('chimere.widgets.SelectMultipleField', [], {'symmetrical': 'False', 'related_name': "'maps'", 'blank': 'True', 'db_table': "'chimere_subcategory_maps'", 'to': "orm['chimere.SubCategory']"}),
+ 'upper_left_corner': ('django.contrib.gis.db.models.fields.PointField', [], {'default': "'POINT(0 0)'"}),
+ 'urn': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'blank': 'True'}),
+ 'users': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'through': "orm['chimere.MapUsers']", 'symmetrical': 'False'}),
+ 'welcome_message': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.maplayers': {
+ 'Meta': {'ordering': "('order',)", 'object_name': 'MapLayers'},
+ 'default': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.Layer']"}),
+ 'map': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.Map']"}),
+ 'order': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.mapusers': {
+ 'Meta': {'object_name': 'MapUsers'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'map': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'mapusers'", 'to': "orm['chimere.Map']"}),
+ 'propose': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'read': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'mapusers'", 'to': "orm['auth.User']"}),
+ 'write': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'chimere.marker': {
+ 'Meta': {'ordering': "('status', 'name')", 'object_name': 'Marker'},
+ 'available_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'categories': ('chimere.widgets.SelectMultipleField', [], {'to': "orm['chimere.SubCategory']", 'symmetrical': 'False'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'end_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'import_key': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'import_source': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'import_version': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'is_front_page': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'license': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'modified_since_import': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'not_for_osm': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'origin': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'point': ('chimere.widgets.PointField', [], {}),
+ 'ref_item': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'submited_marker'", 'null': 'True', 'to': "orm['chimere.Marker']"}),
+ 'route': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'associated_marker'", 'null': 'True', 'to': "orm['chimere.Route']"}),
+ 'start_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'status': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'submiter_comment': ('django.db.models.fields.TextField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'submiter_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
+ 'submiter_name': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
+ 'submiter_session_key': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
+ 'weight': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.multimediaextension': {
+ 'Meta': {'object_name': 'MultimediaExtension'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'multimedia_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'extensions'", 'to': "orm['chimere.MultimediaType']"}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '6'})
+ },
+ 'chimere.multimediafile': {
+ 'Meta': {'object_name': 'MultimediaFile'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'marker': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'multimedia_files'", 'to': "orm['chimere.Marker']"}),
+ 'miniature': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'multimedia_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.MultimediaType']", 'null': 'True', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'url': ('django.db.models.fields.URLField', [], {'max_length': '200'})
+ },
+ 'chimere.multimediatype': {
+ 'Meta': {'object_name': 'MultimediaType'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'iframe': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'media_type': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'mime_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.news': {
+ 'Meta': {'object_name': 'News'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'content': ('django.db.models.fields.TextField', [], {}),
+ 'date': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_front_page': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'maps': ('chimere.widgets.SelectMultipleField', [], {'symmetrical': 'False', 'to': "orm['chimere.Map']", 'null': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.page': {
+ 'Meta': {'object_name': 'Page'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'content': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mnemonic': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '10', 'null': 'True', 'blank': 'True'}),
+ 'template_path': ('django.db.models.fields.CharField', [], {'max_length': '150', 'null': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.picturefile': {
+ 'Meta': {'object_name': 'PictureFile'},
+ 'height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'marker': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'pictures'", 'to': "orm['chimere.Marker']"}),
+ 'miniature': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'picture': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
+ 'thumbnailfile': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'thumbnailfile_height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'thumbnailfile_width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.property': {
+ 'Meta': {'object_name': 'Property'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'marker': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.Marker']"}),
+ 'propertymodel': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.PropertyModel']"}),
+ 'value': ('django.db.models.fields.TextField', [], {})
+ },
+ 'chimere.propertymodel': {
+ 'Meta': {'ordering': "('order',)", 'object_name': 'PropertyModel'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mandatory': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {}),
+ 'subcategories': ('chimere.widgets.SelectMultipleField', [], {'symmetrical': 'False', 'related_name': "'properties'", 'blank': 'True', 'to': "orm['chimere.SubCategory']"}),
+ 'type': ('django.db.models.fields.CharField', [], {'max_length': '1'})
+ },
+ 'chimere.propertymodelchoice': {
+ 'Meta': {'object_name': 'PropertyModelChoice'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'propertymodel': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'choices'", 'to': "orm['chimere.PropertyModel']"}),
+ 'value': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.route': {
+ 'Meta': {'ordering': "('status', 'name')", 'object_name': 'Route'},
+ 'associated_file': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.RouteFile']", 'null': 'True', 'blank': 'True'}),
+ 'categories': ('chimere.widgets.SelectMultipleField', [], {'to': "orm['chimere.SubCategory']", 'symmetrical': 'False'}),
+ 'end_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'has_associated_marker': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'import_key': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'import_source': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'import_version': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'license': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'modified_since_import': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'not_for_osm': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'origin': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'picture': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'ref_item': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'submited_route'", 'null': 'True', 'to': "orm['chimere.Route']"}),
+ 'route': ('chimere.widgets.RouteField', [], {}),
+ 'start_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'status': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'submiter_comment': ('django.db.models.fields.TextField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'submiter_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
+ 'submiter_name': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
+ 'submiter_session_key': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
+ 'width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.routefile': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'RouteFile'},
+ 'file_type': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'raw_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+ 'simplified_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.subcategory': {
+ 'Meta': {'ordering': "['category', 'order']", 'object_name': 'SubCategory'},
+ 'as_layer': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'category': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'subcategories'", 'to': "orm['chimere.Category']"}),
+ 'color_theme': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.ColorTheme']", 'null': 'True', 'blank': 'True'}),
+ 'dated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'hover_icon': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'subcat_hovered'", 'null': 'True', 'to': "orm['chimere.Icon']"}),
+ 'icon': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.Icon']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'item_type': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '1000'}),
+ 'routing_warn': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'submission': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'weighted': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'chimere.tinyurl': {
+ 'Meta': {'object_name': 'TinyUrl'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'parameters': ('django.db.models.fields.CharField', [], {'max_length': '500'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['chimere']
\ No newline at end of file
diff --git a/chimere/models.py b/chimere/models.py
index a1d96d0..b791a8b 100644
--- a/chimere/models.py
+++ b/chimere/models.py
@@ -1381,6 +1381,8 @@ class Map(models.Model, SimpleArea):
u"available"))
external_css = models.URLField(_(u"Link to an external CSS"), blank=True,
null=True)
+ cluster = models.BooleanField(u"Clustering map (weight of items are added)",
+ default=False)
public_read = models.BooleanField(_(u"Public can read the map"))
public_propose = models.BooleanField(_(u"Public can propose item to the map"))
public_write = models.BooleanField(_(u"Public can write without moderation to the map"))
diff --git a/chimere/static/chimere/js/jquery.chimere-leaflet.js b/chimere/static/chimere/js/jquery.chimere-leaflet.js
index f364bc5..47bcaf8 100644
--- a/chimere/static/chimere/js/jquery.chimere-leaflet.js
+++ b/chimere/static/chimere/js/jquery.chimere-leaflet.js
@@ -75,6 +75,7 @@ See the file COPYING for details.
projection: 4326,
theme: null,
enable_clustering: false,
+ clustering_map: false, // weigth of items are added
routing: false, // enable routing management
routing_panel_open: function(){
$('#chimere_itinerary_panel').dialog('open');
@@ -135,7 +136,7 @@ See the file COPYING for details.
}
settings.icons = new Object();
- if (settings.enable_clustering){
+ if (settings.clustering_map){
settings.NumberedDivIcon = L.Icon.extend({
options: {
iconUrl: STATIC_URL + 'chimere/img/empty.png',
@@ -418,7 +419,7 @@ See the file COPYING for details.
settings.map.removeLayer(settings.layerVectors);
settings.layerMarkers.clearLayers();
settings.layerVectors.clearLayers();
- if (settings.enable_clustering){
+ if (settings.clustering_map){
for (var i = 0; i < data.features.length; i++) {
var feature = data.features[i];
if (feature.geometry.type == 'Point'){
diff --git a/chimere/templates/chimere/blocks/map.html b/chimere/templates/chimere/blocks/map.html
index e3b94d9..5268fd3 100644
--- a/chimere/templates/chimere/blocks/map.html
+++ b/chimere/templates/chimere/blocks/map.html
@@ -29,6 +29,7 @@
{% if default_map %}
chimere_init_options["default_map"] = new Array({{default_map.upper_left_corner.x}}, {{default_map.upper_left_corner.y}}, {{default_map.lower_right_corner.x}}, {{default_map.lower_right_corner.y}});
{% endif %}
+ {% if clustering_map %}chimere_init_options["clustering_map"] = true;{% endif %}
{% if p_zoom %}chimere_init_options["zoom"] = {{ p_zoom }};{% endif %}
{% if p_lat %}chimere_init_options["lat"] = {{ p_lat }};{% endif %}
{% if p_lon %}chimere_init_options["lon"] = {{ p_lon }};{% endif %}
diff --git a/chimere/templatetags/chimere_tags.py b/chimere/templatetags/chimere_tags.py
index dbb9efc..461cd15 100644
--- a/chimere/templatetags/chimere_tags.py
+++ b/chimere/templatetags/chimere_tags.py
@@ -219,6 +219,8 @@ def map(context, map_id='map'):
map = Map.objects.get(default=True)
except ObjectDoesNotExist:
pass
+ if map:
+ context_data['clustering_map'] = True if map.cluster else False
subcat_layer = SubCategory.objects.filter(as_layer=True, available=True)
if map:
if map.subcategories.count():
@@ -232,7 +234,7 @@ def map(context, map_id='map'):
unicode(map.lower_right_corner.y)))
if map.subcategories.filter(available=True).count() == 1:
context_data['single_category'] = True
- context_data['checked_categories'] = map.subcategories.all()[0].pk
+ context_data['checked_categories'] = [map.subcategories.all()[0].pk]
elif map.default_subcategories.count():
context_data['checked_categories'] = [s.pk
for s in map.default_subcategories.all()]
@@ -256,7 +258,7 @@ bounds.extend(new OpenLayers.LonLat(%f, %f));
if SubCategory.objects.filter(available=True).count():
cat = unicode(SubCategory.objects.filter(available=True
).all()[0].pk)
- context_data['checked_categories'] = cat
+ context_data['checked_categories'] = [cat]
context_data['dynamic_categories'] = 'true' \
if map and map.dynamic_categories else 'false'
if 'request' not in context:
--
cgit v1.2.3
From 6dc5fc52282013ef548ced96c9eab148eeba0748 Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Tue, 18 Jun 2013 15:30:01 +0200
Subject: Work on enable clustering for non clustering map
---
.../static/chimere/js/jquery.chimere-leaflet.js | 78 ++++++++++++++--------
1 file changed, 50 insertions(+), 28 deletions(-)
diff --git a/chimere/static/chimere/js/jquery.chimere-leaflet.js b/chimere/static/chimere/js/jquery.chimere-leaflet.js
index 47bcaf8..73b86a2 100644
--- a/chimere/static/chimere/js/jquery.chimere-leaflet.js
+++ b/chimere/static/chimere/js/jquery.chimere-leaflet.js
@@ -190,11 +190,19 @@ See the file COPYING for details.
}
});
} else {
+ if (settings.enable_clustering){
+ settings.layerClusters = new L.MarkerClusterGroup({
+ showCoverageOnHover: false});
+ }
settings.layerMarkers = L.geoJson(null, {
onEachFeature: function (feature, layer) {
- layer.bindPopup(feature.properties.name);
+ if (feature.properties && feature.properties.name) {
+ layer.bindPopup(feature.properties.name);
+ }
},
pointToLayer: function (feature, latlng) {
+ var marker = null;
+
if (feature.properties.weight){
var fill_color = "#ff7800";
if (feature.properties.colors){
@@ -213,34 +221,41 @@ See the file COPYING for details.
opacity: 1,
fillOpacity: 0.8
};
- return L.circleMarker(latlng, geojsonMarkerOptions);
- }
- var icon_path = MEDIA_URL + feature.properties.icon_path;
- if (!settings.icons.hasOwnProperty(icon_path)){
- var icon_offset = null;
- if (feature.properties.icon_offset){
- icon_offset = feature.properties.icon_offset;
- } else {
- icon_offset = [feature.properties.icon_width/2,
- feature.properties.icon_height];
- }
- var popup_anchor = null;
- if (feature.properties.popup_anchor){
- popup_anchor = feature.properties.popup_anchor;
- } else {
- popup_anchor = [0,
- -feature.properties.icon_height];
+ marker = L.circleMarker(latlng, geojsonMarkerOptions);
+ } else {
+ var icon_path = MEDIA_URL + feature.properties.icon_path;
+ if (!settings.icons.hasOwnProperty(icon_path)){
+ var icon_offset = null;
+ if (feature.properties.icon_offset){
+ icon_offset = feature.properties.icon_offset;
+ } else {
+ icon_offset = [feature.properties.icon_width/2,
+ feature.properties.icon_height];
+ }
+ var popup_anchor = null;
+ if (feature.properties.popup_anchor){
+ popup_anchor = feature.properties.popup_anchor;
+ } else {
+ popup_anchor = [0,
+ -feature.properties.icon_height];
+ }
+ settings.icons[icon_path] = L.icon({
+ iconUrl: icon_path,
+ iconSize: [feature.properties.icon_width,
+ feature.properties.icon_height],
+ iconAnchor: icon_offset,
+ popupAnchor: popup_anchor
+ });
}
- settings.icons[icon_path] = L.icon({
- iconUrl: icon_path,
- iconSize: [feature.properties.icon_width,
- feature.properties.icon_height],
- iconAnchor: icon_offset,
- popupAnchor: popup_anchor
- });
- }
- return L.marker(latlng,
- {icon: settings.icons[icon_path]});
+ marker = L.marker(latlng,
+ {icon: settings.icons[icon_path]});
+ }/*
+ if (settings.enable_clustering){
+ settings.layerClusters.addLayer(marker);
+ return settings.layerClusters;
+ } else {*/
+ return marker;
+ //}
}
}).addTo(map);
}
@@ -419,6 +434,10 @@ See the file COPYING for details.
settings.map.removeLayer(settings.layerVectors);
settings.layerMarkers.clearLayers();
settings.layerVectors.clearLayers();
+ if (settings.enable_clustering && !settings.clustering_map){
+ settings.map.removeLayer(settings.layerClusters);
+ settings.layerClusters.clearLayers();
+ }
if (settings.clustering_map){
for (var i = 0; i < data.features.length; i++) {
var feature = data.features[i];
@@ -463,6 +482,9 @@ See the file COPYING for details.
}
settings.map.addLayer(settings.layerMarkers);
settings.map.addLayer(settings.layerVectors);
+ if (settings.enable_clustering && !settings.clustering_map){
+ settings.map.addLayer(settings.layerClusters);
+ }
},
error: function (data, textStatus, errorThrown) {
settings.layerMarkers.clearLayers();
--
cgit v1.2.3
From 22cb85975464ddeef3e688a0596f5c649e955e95 Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Tue, 18 Jun 2013 17:24:41 +0200
Subject: Make conditon on actions - simplify map rigth management
---
chimere/actions.py | 42 ++++++++++++------
chimere/models.py | 31 +++++++++++++-
chimere/static/chimere/css/styles.css | 4 ++
chimere/templates/chimere/blocks/maps.html | 8 +++-
chimere/templatetags/chimere_tags.py | 2 +-
chimere/views.py | 69 +++++++++++-------------------
6 files changed, 93 insertions(+), 63 deletions(-)
diff --git a/chimere/actions.py b/chimere/actions.py
index 7a4b263..d9ed274 100644
--- a/chimere/actions.py
+++ b/chimere/actions.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-# Copyright (C) 2008-2010 Étienne Loks
+# Copyright (C) 2008-2013 Étienne Loks
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as
@@ -25,41 +25,57 @@ from django.contrib.auth import models
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
-from models import Page
+from chimere.models import Page, Map
class Action:
- def __init__(self, id, path, label, extra_url_args=[]):
+ def __init__(self, id, path, label, extra_url_args=[],
+ condition=None):
self.id, self.path, self.label = id, path, label
self.extra_url_args, self.url = extra_url_args, None
+ self.condition = condition
def update_url(self, map_name):
self.url = reverse(self.path,
args=[map_name + '/' if map_name else ''] + self.extra_url_args)
-default_actions = [(Action('view', 'chimere:index', _('View')), []),
- (Action('contribute', 'chimere:edit', _('Contribute')),
+DEFAULT_ACTIONS = [[Action('view', 'chimere:index', _('View')), []],
+ [Action('contribute', 'chimere:edit', _('Contribute'),
+ condition=lambda user, map_name:bool(
+ Map.getAvailable(user=user, urn=map_name, single=True,
+ edit=True))),
(Action('edit', 'chimere:edit', _('Add a new point of interest')),
Action('edit-route', 'chimere:editroute', _('Add a new route'))),
- ),]
+ ],
+ ]
if settings.CHIMERE_FEEDS:
- default_actions.append((Action('rss', 'chimere:feeds-form',
- _('RSS feeds')), []))
+ DEFAULT_ACTIONS.append([Action('rss', 'chimere:feeds-form',
+ _('RSS feeds')), []])
if settings.EMAIL_HOST:
- default_actions.append((Action('contact', 'chimere:contact',
- _('Contact us')), []),)
+ DEFAULT_ACTIONS.append([Action('contact', 'chimere:contact',
+ _('Contact us')), []],)
-
-def actions(map_name=''):
- acts = default_actions[:]
+def actions(user, map_name='', default_actions=DEFAULT_ACTIONS):
+ acts, idx = [], -1
for act, childs in default_actions:
+ if act.id not in settings.CHIMERE_DEFAULT_ACTIONS:
+ continue
+ idx += 1
+ if act.condition:
+ if not act.condition(user, map_name):
+ continue
act.update_url(map_name)
for child_act in childs:
child_act.update_url(map_name)
+ if "CHIMERE_DEFAULT_ACTION_LABEL" in dir(settings):
+ if len(settings.CHIMERE_DEFAULT_ACTION_LABEL) > idx:
+ act.label = settings.CHIMERE_DEFAULT_ACTION_LABEL[idx]
+ acts.append((act, childs))
for page in Page.objects.filter(available=True).order_by('order'):
act = Action(page.mnemonic, 'chimere:extra_page', page.title,
[page.mnemonic])
act.update_url(map_name)
acts.append((act, []))
+
return acts
diff --git a/chimere/models.py b/chimere/models.py
index b791a8b..f851b9c 100644
--- a/chimere/models.py
+++ b/chimere/models.py
@@ -1398,10 +1398,37 @@ class Map(models.Model, SimpleArea):
verbose_name = _("Map")
@classmethod
- def getAvailable(cls):
+ def getAvailable(cls, user=None, urn=None, single=False, edit=False):
'''Get available maps
'''
- return cls.objects.filter(available=True)
+ map_filter = {'available':True}
+ if urn:
+ map_filter['urn'] = urn
+ elif single:
+ map_filter['default'] = True
+ filters = [{'public_write':True},
+ {'public_propose':True},
+ ] if edit else [
+ {'public_read':True}]
+ if user and user.is_authenticated():
+ filters += [{'mapusers__user':user, 'mapusers__write':True},
+ {'mapusers__user':user, 'mapusers__propose':True},
+ ] if edit else [
+ {'mapusers__user':user, 'mapusers__read':True},]
+ query = None
+ for fltr in filters:
+ fltr.update(map_filter)
+ if not query:
+ query = Q(**fltr)
+ else:
+ query = query | Q(**fltr)
+ maps = cls.objects.filter(query)
+ if single:
+ if not maps.count():
+ return
+ return maps.all()[0]
+ else:
+ return maps.all()
def getWkt(self):
return "SRID=%d;POLYGON((%f %f,%f %f,%f %f,%f %f, %f %f))" % (
diff --git a/chimere/static/chimere/css/styles.css b/chimere/static/chimere/css/styles.css
index c0844df..28ced33 100644
--- a/chimere/static/chimere/css/styles.css
+++ b/chimere/static/chimere/css/styles.css
@@ -299,6 +299,10 @@ ul#action-2 {
padding:0.3em;
}
+#maps-div label{
+ font-weight:bold;
+}
+
#maps{
position:absolute;
z-index:5;
diff --git a/chimere/templates/chimere/blocks/maps.html b/chimere/templates/chimere/blocks/maps.html
index b069660..92bcfdc 100644
--- a/chimere/templates/chimere/blocks/maps.html
+++ b/chimere/templates/chimere/blocks/maps.html
@@ -1,6 +1,7 @@
{% load i18n %}
-{% if maps and maps.count > 1 %}
+{% if maps %}
+ {% if maps|length > 1 %}
{% trans "Maps:" %}
{% if not has_default_map %}-- {% endif %}
@@ -8,7 +9,6 @@
{{map.name}}
{% endfor %}
-
+ {% else %}
+ {{maps.0.name}}
+ {% endif %}
+
{% endif %}
diff --git a/chimere/templatetags/chimere_tags.py b/chimere/templatetags/chimere_tags.py
index 461cd15..406aa0c 100644
--- a/chimere/templatetags/chimere_tags.py
+++ b/chimere/templatetags/chimere_tags.py
@@ -26,7 +26,7 @@ def display_maps(context):
"""
Display available maps.
"""
- context_data = {"maps": Map.getAvailable(),
+ context_data = {"maps": list(Map.getAvailable(user=context['user'])),
"base_url": reverse('chimere:index')
}
if "map_name" in context:
diff --git a/chimere/views.py b/chimere/views.py
index 18039b7..d9231e1 100644
--- a/chimere/views.py
+++ b/chimere/views.py
@@ -45,7 +45,7 @@ from django.utils import simplejson as json
from django.utils.http import urlquote
from django.utils.translation import ugettext as _
-from chimere.actions import actions
+from chimere.actions import actions as default_actions
from chimere.models import Category, SubCategory, PropertyModel, Page,\
Marker, Route, News, SimpleArea, Map, Color, TinyUrl, RouteFile,\
AggregatedRoute
@@ -88,35 +88,12 @@ def get_base_response(request, map_name="", edit=False):
base_url += '/'
base_url += map_name + '/'
base_response_dct['extra_url'] = base_url
- map_filter = {'available':True}
- if map_name:
- map_filter['urn'] = map_name
- else:
- map_filter['default'] = True
- filters = [{'public_write':True},
- {'public_propose':True},
- ] if edit else [
- {'public_read':True}]
- if base_response_dct['is_authenticated']:
- filters += [{'mapusers__user':request.user, 'mapusers__write':True},
- {'mapusers__user':request.user, 'mapusers__propose':True},
- ] if edit else [
- {'mapusers__user':request.user, 'mapusers__read':True},]
- query = None
- for fltr in filters:
- fltr.update(map_filter)
- if not query:
- query = Q(**fltr)
- else:
- query = query | Q(**fltr)
- map = None
- try:
- map = Map.objects.get(query)
+ map = Map.getAvailable(user=request.user, urn=map_name, edit=edit,
+ single=True)
+ if map:
map_name = map.urn
- except ObjectDoesNotExist:
- if map_name:
- return None, redirect(reverse('chimere:index'))
-
+ elif map_name:
+ return None, redirect(reverse('chimere:index'))
if edit and map:
base_response_dct['can_write'] = bool(map.public_write or
Map.objects.filter(pk=map.pk, mapusers__user=request.user,
@@ -164,7 +141,7 @@ def logout_view(request):
return redirect(reverse('chimere:index'))
def index(request, map_name=None, default_map=None, simple=False,
- get_response=False):
+ get_response=False, actions=default_actions):
"""
Main page
"""
@@ -215,7 +192,7 @@ def index(request, map_name=None, default_map=None, simple=False,
except:
pass
response_dct.update({
- 'actions':actions(response_dct['map_name']),
+ 'actions':actions(request.user, response_dct['map_name']),
'action_selected':('view',),
'error_message':'',
'news_visible': news_visible,
@@ -351,7 +328,8 @@ def get_edit_page(redirect_url, item_cls, item_form,
get_edit_marker = get_edit_page('chimere:edit', Marker, MarkerForm)
-def edit(request, map_name="", item_id=None, submited=False):
+def edit(request, map_name="", item_id=None, submited=False,
+ actions=default_actions):
"""
Edition page
"""
@@ -370,7 +348,7 @@ def edit(request, map_name="", item_id=None, submited=False):
if request.POST and request.POST.get('point'):
point_value = request.POST.get('point')
response_dct.update({
- 'actions':actions(response_dct['map_name']),
+ 'actions':actions(request.user, response_dct['map_name']),
'action_selected':('contribute', 'edit'),
'map_layer':settings.CHIMERE_DEFAULT_MAP_LAYER,
'form':form,
@@ -459,7 +437,8 @@ def processRouteFile(request, map_name='', file_id=None):
get_edit_route = get_edit_page('chimere:editroute', Route, RouteForm)
-def editRoute(request, map_name="", item_id=None, submited=False):
+def editRoute(request, map_name="", item_id=None, submited=False,
+ actions=default_actions):
"""
Route edition page
"""
@@ -478,7 +457,7 @@ def editRoute(request, map_name="", item_id=None, submited=False):
if request.POST and request.POST.get('route'):
route_value = request.POST.get('route')
response_dct.update({
- 'actions':actions(response_dct['map_name']),
+ 'actions':actions(request.user, response_dct['map_name']),
'action_selected':('contribute', 'edit-route'),
'error_message':'',
'map_layer':settings.CHIMERE_DEFAULT_MAP_LAYER,
@@ -499,31 +478,31 @@ def editRoute(request, map_name="", item_id=None, submited=False):
return render_to_response('chimere/edit_route.html', response_dct,
context_instance=RequestContext(request))
-def submited(request, map_name="", action=""):
+def submited(request, map_name="", action="", actions=default_actions):
"""
Successful submission page
"""
response_dct, redir = get_base_response(request, map_name)
if redir:
return redir
- response_dct.update({'actions':actions(response_dct['map_name']),
+ response_dct.update({'actions':actions(request.user, response_dct['map_name']),
'action_selected':action,})
return render_to_response('chimere/submited.html', response_dct,
context_instance=RequestContext(request))
-def charte(request, map_name=""):
+def charte(request, map_name="", actions=default_actions):
"""
Affichage de la charte
"""
response_dct, redir = get_base_response(request, map_name)
if redir:
return redir
- response_dct.update({'actions':actions(response_dct['map_name']),
+ response_dct.update({'actions':actions(request.user, response_dct['map_name']),
'action_selected':('charte',)})
return render_to_response('chimere/charte.html', response_dct,
context_instance=RequestContext(request))
-def contactus(request, map_name=""):
+def contactus(request, map_name="", actions=default_actions):
"""
Contact page
"""
@@ -547,13 +526,13 @@ def contactus(request, map_name=""):
response_dct, redir = get_base_response(request, map_name)
if redir:
return redir
- response_dct.update({'actions':actions(response_dct['map_name']),
+ response_dct.update({'actions':actions(request.user, response_dct['map_name']),
'action_selected':('contact',),
'contact_form':form, 'message':msg})
return render_to_response('chimere/contactus.html', response_dct,
context_instance=RequestContext(request))
-def extraPage(request, map_name="", page_id=""):
+def extraPage(request, map_name="", page_id="", actions=default_actions):
"""
Extra dynamic pages
"""
@@ -564,7 +543,7 @@ def extraPage(request, map_name="", page_id=""):
response_dct, redir = get_base_response(request, map_name)
if redir:
return redir
- response_dct.update({'actions':actions(response_dct['map_name']),
+ response_dct.update({'actions':actions(request.user, response_dct['map_name']),
'action_selected':(page_id,),
'content':page.content,
'title':page.title})
@@ -880,14 +859,14 @@ def get_json(request, map_name='', app_name='', filename=''):
return HttpResponse(open(settings.STATIC_ROOT+app_name+'/json/'+filename+'.json'),
'application/javascript', status=200)
-def rss(request, map_name=''):
+def rss(request, map_name='', actions=default_actions):
'''
Redirect to RSS subscription page
'''
response_dct, redir = get_base_response(request, map_name)
if redir:
return redir
- response_dct.update({'actions':actions(response_dct['map_name']),
+ response_dct.update({'actions':actions(request.user, response_dct['map_name']),
'action_selected':('rss',),
'category_rss_feed':'',})
# If the form has been submited
--
cgit v1.2.3
From fd0c17b2f79c8167fd44677985a76dec6a2b4b40 Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Tue, 18 Jun 2013 17:58:48 +0200
Subject: Default to Openlayers for edition
---
chimere/templates/chimere/edit.html | 2 +-
chimere/templatetags/chimere_tags.py | 2 +-
chimere/widgets.py | 11 +++++++----
3 files changed, 9 insertions(+), 6 deletions(-)
diff --git a/chimere/templates/chimere/edit.html b/chimere/templates/chimere/edit.html
index f9706d3..509e740 100644
--- a/chimere/templates/chimere/edit.html
+++ b/chimere/templates/chimere/edit.html
@@ -3,7 +3,7 @@
{% block extra_head %}
{{ form.media }}
{{ block.super }}
- {% head_chimere %}
+ {% head_chimere False %}
{% if dated %}
diff --git a/chimere/templatetags/chimere_tags.py b/chimere/templatetags/chimere_tags.py
index 406aa0c..b285bfd 100644
--- a/chimere/templatetags/chimere_tags.py
+++ b/chimere/templatetags/chimere_tags.py
@@ -143,7 +143,7 @@ def head_chimere(context, view=True):
"DEFAULT_CENTER": settings.CHIMERE_DEFAULT_CENTER,
"DEFAULT_ZOOM": settings.CHIMERE_DEFAULT_ZOOM,
"MAP_LAYER": settings.CHIMERE_DEFAULT_MAP_LAYER,
- "CHIMERE_VIEW_RENDERER": settings.CHIMERE_VIEW_RENDERER,
+ "CHIMERE_VIEW_RENDERER": map_renderer,
"MAP_CONDITIONNAL_CSS_URLS": settings.MAP_CONDITIONNAL_CSS_URLS[map_renderer],
"MAP_JS_URLS": settings.MAP_JS_URLS[map_renderer],
'routing': settings.CHIMERE_ENABLE_ROUTING \
diff --git a/chimere/widgets.py b/chimere/widgets.py
index 8416690..a9aa3dc 100644
--- a/chimere/widgets.py
+++ b/chimere/widgets.py
@@ -59,7 +59,7 @@ def getMapJS(map_name=''):
u"%s// !-->\n" % js
return js
-def get_map_layers(map_name=''):
+def get_map_layers(map_name='', force_default=False):
from chimere.models import Map
map = None
if map_name:
@@ -73,7 +73,7 @@ def get_map_layers(map_name=''):
except ObjectDoesNotExist:
pass
map_layers, default = [], None
- if map and map.layers.count():
+ if not force_default and map and map.layers.count():
map_layers = [[layer.name, layer.layer_code, False]
for layer in map.layers.order_by('maplayers__order').all()]
def_layer = map.layers.filter(maplayers__default=True)
@@ -85,7 +85,7 @@ def get_map_layers(map_name=''):
map_layers[order][2] = True
else:
map_layers[0][2] = True
- elif settings.CHIMERE_DEFAULT_MAP_LAYER:
+ elif not force_default and settings.CHIMERE_DEFAULT_MAP_LAYER:
map_layers = [(_(u"Default layer"), settings.CHIMERE_DEFAULT_MAP_LAYER,
True)]
else:
@@ -273,7 +273,10 @@ class PointChooserWidget(forms.TextInput):
value = None
else:
value = None
- map_layers, default_map = get_map_layers(map_name)
+ force = False
+ if settings.CHIMERE_EDIT_RENDERER != settings.CHIMERE_VIEW_RENDERER:
+ force = True
+ map_layers, default_map = get_map_layers(map_name, force_default=force)
map_layers = [js for n, js, default in map_layers]
#TODO: manage maps
return mark_safe(
--
cgit v1.2.3
From e258552038a630cdfde44b3e39ec24d72914f180 Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Tue, 18 Jun 2013 18:59:14 +0200
Subject: Leaflet: display details on popup click
---
.../static/chimere/js/jquery.chimere-leaflet.js | 35 +++++++++++++++++++++-
1 file changed, 34 insertions(+), 1 deletion(-)
diff --git a/chimere/static/chimere/js/jquery.chimere-leaflet.js b/chimere/static/chimere/js/jquery.chimere-leaflet.js
index 73b86a2..c75a3b7 100644
--- a/chimere/static/chimere/js/jquery.chimere-leaflet.js
+++ b/chimere/static/chimere/js/jquery.chimere-leaflet.js
@@ -254,10 +254,15 @@ See the file COPYING for details.
settings.layerClusters.addLayer(marker);
return settings.layerClusters;
} else {*/
+ marker.properties = feature.properties;
return marker;
//}
}
}).addTo(map);
+ map.on('popupopen', function(e) {
+ var marker = e.popup._source;
+ methods.display_feature_detail(marker.properties.pk);
+ });
}
settings.layerMarkers.addTo(map);
settings.layerVectors = L.geoJson().addTo(map);
@@ -553,7 +558,35 @@ See the file COPYING for details.
} else if ($.hasattr("area", options)) {
helpers.zoom_to_area(options["area"]);
}
- } // end of init
+ },
+ display_feature_detail: function (pk) {
+ /*
+ * update current detail panel with an AJAX request
+ */
+ var uri = extra_url
+ if (settings.map_id) uri += settings.map_id + "/"
+ uri += "getDetail/" + pk;
+ var params = {}
+ if (settings.simple) { params["simple"] = 1; }
+ $.ajax({url: uri,
+ data: params,
+ dataType: "html",
+ success: function (data) {
+ if ( settings.display_feature_detail_fx ) {
+ // Custom function ?
+ settings.display_feature_detail_fx(data, settings);
+ }
+ else {
+ if (!settings.popupContentFull) {
+ $('#detail').html(data).show();
+ }
+ else {
+ settings.current_popup.setContentHTML("" + data + "
");
+ }
+ }
+ }
+ });
+ },
}; // End of public methods
var helpers = {
getSubcategories: function (category_id) {
--
cgit v1.2.3
From 2a52b938ec9ed46f609a5df850bf39965995da43 Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Tue, 18 Jun 2013 19:00:00 +0200
Subject: Manage edition rights
---
chimere/templates/chimere/detail.html | 5 ++++-
chimere/views.py | 14 +++++++++++---
2 files changed, 15 insertions(+), 4 deletions(-)
diff --git a/chimere/templates/chimere/detail.html b/chimere/templates/chimere/detail.html
index f547785..4772d37 100644
--- a/chimere/templates/chimere/detail.html
+++ b/chimere/templates/chimere/detail.html
@@ -28,9 +28,12 @@
{% trans "Show multimedia gallery" %}
{% endif %}
+ {% if can_edit or can_propose %}
- {% trans "Submit an amendment" %}
+ {% if can_write %}{% trans "Edit" %}{%else%}
+ {% trans "Submit an amendment" %}{%endif%}
+ {% endif %}
{% if moderator_emails %}
{% trans "Propose amendment" %}
diff --git a/chimere/views.py b/chimere/views.py
index d9231e1..8964321 100644
--- a/chimere/views.py
+++ b/chimere/views.py
@@ -94,10 +94,18 @@ def get_base_response(request, map_name="", edit=False):
map_name = map.urn
elif map_name:
return None, redirect(reverse('chimere:index'))
- if edit and map:
- base_response_dct['can_write'] = bool(map.public_write or
+ if map:
+ base_response_dct['can_write'] = map.public_write
+ base_response_dct['can_propose'] = map.public_propose
+ if base_response_dct['is_authenticated']:
+ base_response_dct['can_write'] = \
+ base_response_dct['can_write'] or bool(
Map.objects.filter(pk=map.pk, mapusers__user=request.user,
mapusers__write=True).count())
+ base_response_dct['can_propose'] = \
+ base_response_dct['can_propose'] or bool(
+ Map.objects.filter(pk=map.pk, mapusers__user=request.user,
+ mapusers__propose=True).count())
base_response_dct['map'] = map
base_response_dct['map_name'] = map_name
@@ -201,7 +209,7 @@ def index(request, map_name=None, default_map=None, simple=False,
'dynamic_categories':response_dct['dynamic_categories'],
'zoomout':zoomout,
'has_default_map':Map.objects.filter(default=True).count(),
- 'zoomout':zoomout
+ 'zoomout':zoomout,
})
if hasattr(settings, 'CONTACT_EMAIL') and settings.CONTACT_EMAIL:
response_dct['contact_email'] = settings.CONTACT_EMAIL
--
cgit v1.2.3
From 5b0031b8144ac88626fbf1d7c6b5be9b4e662e29 Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Tue, 18 Jun 2013 19:13:58 +0200
Subject: Update french translation
---
chimere/locale/fr/LC_MESSAGES/django.po | 569 +++++++++++++++++++-------------
1 file changed, 336 insertions(+), 233 deletions(-)
diff --git a/chimere/locale/fr/LC_MESSAGES/django.po b/chimere/locale/fr/LC_MESSAGES/django.po
index f0b15b1..13ca044 100644
--- a/chimere/locale/fr/LC_MESSAGES/django.po
+++ b/chimere/locale/fr/LC_MESSAGES/django.po
@@ -7,34 +7,34 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.2\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2013-03-14 18:43+0100\n"
+"POT-Creation-Date: 2013-06-18 19:06+0200\n"
"PO-Revision-Date: 2010-03-20 20:00+0100\n"
"Last-Translator: Étienne Loks \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-#: actions.py:39
+#: actions.py:41
msgid "View"
msgstr "Voir"
-#: actions.py:40
+#: actions.py:42
msgid "Contribute"
msgstr "Participer"
-#: actions.py:41
+#: actions.py:46
msgid "Add a new point of interest"
msgstr "Ajout d'un point remarquable"
-#: actions.py:42
+#: actions.py:47
msgid "Add a new route"
msgstr "Ajout d'un nouveau trajet"
-#: actions.py:47
+#: actions.py:53
msgid "RSS feeds"
msgstr "Flux RSS"
-#: actions.py:51
+#: actions.py:57
msgid "Contact us"
msgstr "Nous contacter"
@@ -55,77 +55,77 @@ msgstr "Exporter en KML"
msgid "Export to Shapefile"
msgstr "Exporter en Shapefile"
-#: admin.py:95
+#: admin.py:99
msgid "Export to CSV"
msgstr "Exporter en CSV"
-#: admin.py:102
+#: admin.py:106
msgid "Only one item can be managed at a time."
msgstr "Seul un élément à la fois peut-être géré."
-#: admin.py:112
+#: admin.py:116
msgid "No modified item associated to the selected item."
msgstr "Pas d'élément modifié associé à l'élément sélectionné."
-#: admin.py:158
+#: admin.py:162
msgid "Modified item traited."
msgstr "Élément modifié traité."
-#: admin.py:163
+#: admin.py:167
msgid "Managed modified items"
msgstr "Gérer les éléments modifiés"
-#: admin.py:197 admin.py:274
+#: admin.py:201 admin.py:277
msgid "Submitter"
msgstr "Demandeur"
-#: admin.py:202 admin.py:279 admin.py:331
+#: admin.py:206 admin.py:282 admin.py:338
msgid "Import"
msgstr "Import"
-#: admin.py:207 admin.py:284
+#: admin.py:211 admin.py:287
msgid "Associated items"
msgstr "Éléments associés"
-#: admin.py:337
+#: admin.py:344
msgid "Cancel import"
msgstr "Annuler l'import"
-#: admin.py:343
+#: admin.py:350
msgid "Cancel export"
msgstr "Annuler l'export"
-#: admin.py:347
+#: admin.py:354
msgid "Can manage only one OSM export at a time."
msgstr "Ne peux gérer qu'un seul export OSM à la fois."
-#: admin.py:352
+#: admin.py:359
msgid ""
"You must treat all item with the status \"imported\" before exporting to OSM."
msgstr ""
"Vous devez traiter tous les éléments avec le status « importé » avant "
"d'exporter vers OSM."
-#: admin.py:356
+#: admin.py:363
msgid "Only OSM importer are managed for export."
msgstr "Seul les imports de type OSM peuvent être gérés pour les exports."
-#: admin.py:363
+#: admin.py:370
msgid "No point of interest are concerned by this export."
msgstr "Aucun point d'intérêt n'est concerné par cet export."
-#: admin.py:375
+#: admin.py:382
msgid "Export launched."
msgstr "Export lancé."
-#: admin.py:379
+#: admin.py:386
#, python-format
msgid ""
"%s point(s) of interest concerned by this export before bounding box filter."
msgstr ""
"%s point(s) d'intérêt concerné par cet export (avant le filtre sur la zone)"
-#: admin.py:384
+#: admin.py:391
msgid "Export to osm"
msgstr "Exporter vers osm"
@@ -166,7 +166,7 @@ msgstr "Objet"
msgid "OSM user"
msgstr "Utilisateur OSM"
-#: forms.py:118 models.py:1500
+#: forms.py:118 models.py:1592
msgid "Password"
msgstr "Mot de passe"
@@ -227,16 +227,16 @@ msgstr "Fichier"
msgid "Bad file format: this must be a GPX or KML file"
msgstr "Mauvais format de fichier : KML et GPX sont supportés"
-#: forms.py:512 models.py:53 models.py:101 models.py:163 models.py:184
-#: models.py:197 models.py:212 models.py:375 models.py:720 models.py:776
-#: models.py:835 models.py:953 models.py:1304 models.py:1316 models.py:1490
+#: forms.py:512 models.py:53 models.py:101 models.py:163 models.py:185
+#: models.py:198 models.py:213 models.py:378 models.py:752 models.py:808
+#: models.py:867 models.py:985 models.py:1343 models.py:1355 models.py:1582
#: utils.py:485 templates/admin/chimere/managed_modified.html:23
#: templates/chimere/edit.html:40 templates/chimere/edit_route.html:37
#: templates/chimere/blocks/alternate_multimedia.html:39
msgid "Name"
msgstr "Nom"
-#: forms.py:521 models.py:1353
+#: forms.py:521
msgid "Area"
msgstr "Zone"
@@ -250,15 +250,15 @@ msgid "The area \"%s\" has the same order, you need to choose another one."
msgstr ""
"La zone « %s » a le même numéro d'ordre, vous devez un choisir un autre."
-#: forms.py:613
+#: forms.py:619
msgid "Start"
msgstr "Départ"
-#: forms.py:614
+#: forms.py:620
msgid "Finish"
msgstr "Arrivée"
-#: forms.py:615
+#: forms.py:621
msgid "Speed"
msgstr "Vitesse"
@@ -266,13 +266,13 @@ msgstr "Vitesse"
msgid "Mnemonic"
msgstr "Mnémonique"
-#: models.py:56 models.py:102 models.py:185 models.py:213 models.py:368
-#: models.py:724 models.py:1322 models.py:1492 models.py:1533
+#: models.py:56 models.py:102 models.py:186 models.py:214 models.py:371
+#: models.py:756 models.py:1356 models.py:1584 models.py:1625
msgid "Available"
msgstr "Disponible"
-#: models.py:57 models.py:173 models.py:186 models.py:230 models.py:778
-#: models.py:850 models.py:1321 models.py:1479 models.py:1491
+#: models.py:57 models.py:173 models.py:187 models.py:233 models.py:810
+#: models.py:882 models.py:1362 models.py:1571 models.py:1583
msgid "Order"
msgstr "Ordre"
@@ -284,21 +284,21 @@ msgstr "Chemin du patron"
msgid "Page"
msgstr "Page"
-#: models.py:103 models.py:469
+#: models.py:103 models.py:474
msgid "Is front page"
msgstr "Est en page principale"
-#: models.py:105 models.py:1501
+#: models.py:105 models.py:1593
msgid "Date"
msgstr "Date"
-#: models.py:107 models.py:777
+#: models.py:107 models.py:809
msgid "Url"
msgstr "Url"
#: models.py:108
-msgid "Associated areas"
-msgstr "Zones associées"
+msgid "Associated maps"
+msgstr "Cartes associées"
#: models.py:114 models.py:115 templates/chimere/blocks/news.html:3
#: templates/chimere/blocks/news.html:5
@@ -313,7 +313,7 @@ msgstr "Paramètres"
msgid "TinyUrl"
msgstr "Mini-url"
-#: models.py:167 models.py:174 models.py:225
+#: models.py:167 models.py:174 models.py:228
msgid "Color theme"
msgstr "Thème de couleur"
@@ -321,401 +321,409 @@ msgstr "Thème de couleur"
msgid "Code"
msgstr "Code"
-#: models.py:179
+#: models.py:180
msgid "Color"
msgstr "Couleur"
-#: models.py:192 models.py:210
+#: models.py:193 models.py:211
msgid "Category"
msgstr "Catégorie"
-#: models.py:198 models.py:716 models.py:836 models.py:1019
+#: models.py:199 models.py:748 models.py:868 models.py:1051
#: templates/chimere/blocks/alternate_multimedia.html:43
msgid "Image"
msgstr "Image"
-#: models.py:200 models.py:838 models.py:1021
+#: models.py:201 models.py:870 models.py:1053
msgid "Height"
msgstr "Hauteur"
-#: models.py:201 models.py:839 models.py:1022
+#: models.py:202 models.py:871 models.py:1054
msgid "Width"
msgstr "Largeur"
-#: models.py:205 models.py:222
+#: models.py:206 models.py:225
msgid "Icon"
msgstr "Icône"
-#: models.py:214
+#: models.py:215
msgid "Available for submission"
msgstr "Disponible pour soumission"
-#: models.py:216
+#: models.py:217
+msgid "Has an associated quantity"
+msgstr "A une quantité associée"
+
+#: models.py:219
msgid "Marker"
msgstr "Point d'intérêt"
-#: models.py:217 models.py:1015 models.py:1032
+#: models.py:220 models.py:1047 models.py:1064
#: templates/chimere/edit_route.html:28
msgid "Route"
msgstr "Trajet"
-#: models.py:218
+#: models.py:221
msgid "Both"
msgstr "Mixte"
-#: models.py:219
+#: models.py:222
msgid "Item type"
msgstr "Type d'élément"
-#: models.py:220
+#: models.py:223
msgid "Is dated"
msgstr "Est daté"
-#: models.py:223
+#: models.py:226
msgid "Hover icon"
msgstr "Icône en survol"
-#: models.py:227
+#: models.py:230
msgid "Displayed in the layer menu"
msgstr "Apparaît dans le menu des couches ?"
-#: models.py:229
+#: models.py:232
msgid "Routing warn"
msgstr "Avertissement sur les itinéraires"
-#: models.py:235
+#: models.py:238
msgid "Sub-category"
msgstr "Sous-catégorie"
-#: models.py:236
+#: models.py:239
msgid "Sub-categories"
msgstr "Sous-catégories"
-#: models.py:320
+#: models.py:323
msgid "Importer type"
msgstr "Type d'import"
-#: models.py:322
+#: models.py:325
msgid "Filter"
msgstr "Filtre"
-#: models.py:324 templates/chimere/blocks/alternate_multimedia.html:49
+#: models.py:327 templates/chimere/blocks/alternate_multimedia.html:49
msgid "Web address"
msgstr "Adresse web"
-#: models.py:326
+#: models.py:329
msgid "Source file"
msgstr "Fichier source"
-#: models.py:328
+#: models.py:331
msgid "Name by default"
msgstr "Nom par défaut"
-#: models.py:330
+#: models.py:333
msgid "SRID"
msgstr "SRID"
-#: models.py:331
+#: models.py:334
msgid "Zipped file"
msgstr "Fichier zippé"
-#: models.py:332
+#: models.py:335
msgid "Overwrite existing data"
msgstr "Écraser les données existantes"
-#: models.py:334
+#: models.py:337
msgid "Get description from source"
msgstr "Obtenir une description depuis la source"
-#: models.py:336
+#: models.py:339
msgid "Default description"
msgstr "Description par défaut"
-#: models.py:338 models.py:396
+#: models.py:341 models.py:399
msgid "Origin"
msgstr "Origine"
-#: models.py:340 models.py:398
+#: models.py:343 models.py:401
msgid "License"
msgstr "Licence"
-#: models.py:343
+#: models.py:346
msgid "Associated subcategories"
msgstr "Sous-catégories associées"
-#: models.py:344 utils.py:489
+#: models.py:347 utils.py:489
msgid "State"
msgstr "État"
-#: models.py:346
+#: models.py:349
msgid "Automatically associate a marker to a way"
msgstr "Associer automatiquement un marqueur à une route"
-#: models.py:350
+#: models.py:353
msgid "Importer"
msgstr "Import"
-#: models.py:367
+#: models.py:370
msgid "Submited"
msgstr "Soumis"
-#: models.py:369
+#: models.py:372
msgid "Modified"
msgstr "Modifié"
-#: models.py:370
+#: models.py:373
msgid "Disabled"
msgstr "Désactivé"
-#: models.py:371
+#: models.py:374
msgid "Imported"
msgstr "Importé"
-#: models.py:377
+#: models.py:380
msgid "Submitter session key"
msgstr "Clé de session du demandeur"
-#: models.py:379
+#: models.py:382
msgid "Submitter name or nickname"
msgstr "Nom ou pseudo du demandeur"
-#: models.py:381
+#: models.py:384
msgid "Submitter email"
msgstr "Courriel du demandeur"
-#: models.py:383
+#: models.py:386
msgid "Submitter comment"
msgstr "Commentaire du demandeur"
-#: models.py:385 models.py:1195
+#: models.py:388 models.py:1234
msgid "Status"
msgstr "État"
-#: models.py:386
+#: models.py:389
msgid "Import key"
msgstr "Clé d'import"
-#: models.py:388
+#: models.py:391
msgid "Import version"
msgstr "Version de l'import"
-#: models.py:390
+#: models.py:393
msgid "Source"
msgstr "Source"
-#: models.py:392
+#: models.py:395
msgid "Modified since last import"
msgstr "Modifié depuis le dernier import"
-#: models.py:394
+#: models.py:397
msgid "Not to be exported to OSM"
msgstr "À ne pas exporter vers OSM"
-#: models.py:400 templates/chimere/edit.html:57
+#: models.py:403 templates/chimere/edit.html:57
#: templates/chimere/edit_route.html:53
msgid "Start date"
msgstr "Date de début"
-#: models.py:401
+#: models.py:404
msgid "Not mandatory. Set it for dated item such as event. Format YYYY-MM-DD"
msgstr ""
"Optionnel. Précisez ce champ pour les éléments datés comme un événement. "
"Format du champ : AAAA-MM-JJ"
-#: models.py:403 templates/chimere/edit.html:63
+#: models.py:406 templates/chimere/edit.html:63
#: templates/chimere/edit_route.html:59
msgid "End date"
msgstr "Date de fin"
-#: models.py:404
+#: models.py:407
msgid ""
"Not mandatory. Set it only if you have a multi-day event. Format YYYY-MM-DD"
msgstr ""
"Optionnel. Précisez ce champ seulement pour des événements durant plusieurs "
"jours. Format du champ : AAAA-MM-JJ"
-#: models.py:461
+#: models.py:464
msgid "Reference marker"
msgstr "Point d'intérêt de référence"
-#: models.py:462 utils.py:491
+#: models.py:465 utils.py:491
msgid "Localisation"
msgstr "Localisation"
-#: models.py:464
+#: models.py:467
msgid "Available Date"
msgstr "Date de mise en disponibilité"
-#: models.py:468 utils.py:490 templates/admin/chimere/managed_modified.html:31
+#: models.py:471
+msgid "Quantity"
+msgstr "Quantité"
+
+#: models.py:473 utils.py:490 templates/admin/chimere/managed_modified.html:31
#: templates/chimere/edit.html:50 templates/chimere/edit_route.html:47
msgid "Description"
msgstr "Description"
-#: models.py:539 models.py:1543
+#: models.py:548 models.py:1635
msgid "Point of interest"
msgstr "Point d'intérêt"
-#: models.py:714
+#: models.py:746
msgid "Audio"
msgstr "Audio"
-#: models.py:715
+#: models.py:747
msgid "Video"
msgstr "Vidéo"
-#: models.py:717
+#: models.py:749
msgid "Other"
msgstr "Autre"
-#: models.py:718
+#: models.py:750
msgid "Media type"
msgstr "Type de media"
-#: models.py:721
+#: models.py:753
msgid "Mime type"
msgstr "Type mime"
-#: models.py:723
+#: models.py:755
msgid "Inside an iframe"
msgstr "À l'intérieur d'un iframe"
-#: models.py:727
+#: models.py:759
msgid "Multimedia type"
msgstr "Type de multimedia"
-#: models.py:728
+#: models.py:760
msgid "Multimedia types"
msgstr "Types de multimedia"
-#: models.py:737
+#: models.py:769
msgid "Automatic recognition"
msgstr "Reconnaissance automatique"
-#: models.py:763
+#: models.py:795
msgid "Extension name"
msgstr "Nom de l'extension"
-#: models.py:765
+#: models.py:797
msgid "Associated multimedia type"
msgstr "Type de multimedia associé"
-#: models.py:769
+#: models.py:801
msgid "Multimedia extension"
msgstr "Extension multimedia"
-#: models.py:770
+#: models.py:802
msgid "Multimedia extensions"
msgstr "Extensions multimedia"
-#: models.py:780 models.py:840
+#: models.py:812 models.py:872
msgid "Display inside the description?"
msgstr "Apparaît dans la description ?"
-#: models.py:785
+#: models.py:817
msgid "Multimedia file"
msgstr "Fichier multimedia"
-#: models.py:786
+#: models.py:818
msgid "Multimedia files"
msgstr "Fichiers multimedias"
-#: models.py:842
+#: models.py:874
msgid "Thumbnail"
msgstr "Miniature"
-#: models.py:846
+#: models.py:878
msgid "Thumbnail height"
msgstr "Hauteur de la miniature"
-#: models.py:848
+#: models.py:880
msgid "Thumbnail width"
msgstr "Largeur de la miniature"
-#: models.py:857
+#: models.py:889
msgid "Picture file"
msgstr "Fichier d'image"
-#: models.py:858
+#: models.py:890
msgid "Picture files"
msgstr "Fichiers d'image"
-#: models.py:954
+#: models.py:986
msgid "Raw file (gpx or kml)"
msgstr "Fichier brut (gpx ou kml)"
-#: models.py:956
+#: models.py:988
msgid "Simplified file"
msgstr "Fichier simplifié"
-#: models.py:958
+#: models.py:990
msgid "KML"
msgstr "KML"
-#: models.py:958
+#: models.py:990
msgid "GPX"
msgstr "GPX"
-#: models.py:963
+#: models.py:995
msgid "Route file"
msgstr "Fichier de trajet"
-#: models.py:964
+#: models.py:996
msgid "Route files"
msgstr "Fichiers de trajet"
-#: models.py:1014
+#: models.py:1046
msgid "Reference route"
msgstr "Trajet de référence"
-#: models.py:1018
+#: models.py:1050
msgid "Associated file"
msgstr "Fichier associé"
-#: models.py:1023
+#: models.py:1055
msgid "Has an associated marker"
msgstr "Dispose d'un marqueur associé"
-#: models.py:1305
+#: models.py:1344
msgid "Layer code"
msgstr "Code pour la couche"
-#: models.py:1311
+#: models.py:1350
msgid "Layer"
msgstr "Couche"
-#: models.py:1317
-msgid "Area urn"
-msgstr "Urn de la zone"
+#: models.py:1358
+msgid "Map urn"
+msgstr "Urn de la carte"
-#: models.py:1319 templates/chimere/blocks/welcome.html:3
+#: models.py:1360 templates/chimere/blocks/welcome.html:3
msgid "Welcome message"
msgstr "Message d'accueil"
-#: models.py:1323
+#: models.py:1363
msgid "Upper left corner"
msgstr "Coin en haut à gauche"
-#: models.py:1325
+#: models.py:1365
msgid "Lower right corner"
msgstr "Coin en bas à droite"
-#: models.py:1327
-msgid "Default area"
-msgstr "Zone par défaut"
+#: models.py:1367
+msgid "Default map"
+msgstr "Carte par défaut"
-#: models.py:1328
-msgid "Only one area is set by default"
-msgstr "Seule une zone est définie par défaut"
+#: models.py:1368
+msgid "Only one map is set by default"
+msgstr "Seule une carte est définie par défaut"
-#: models.py:1332
+#: models.py:1372
msgid "Sub-categories checked by default"
msgstr "Sous-catégories cochées par défaut"
-#: models.py:1334
+#: models.py:1374
msgid "Sub-categories dynamicaly displayed"
msgstr "Sous-categories affichées dynamiquement"
-#: models.py:1335
+#: models.py:1375
msgid ""
"If checked, categories are only displayed in the menu if they are available "
"on the current extent."
@@ -723,75 +731,139 @@ msgstr ""
"Si coché, les catégories sont disponibles sur le menu seulement si elles "
"apparaissent sur la zone affichée."
-#: models.py:1339 models.py:1495
+#: models.py:1379 models.py:1587
msgid "Restricted to theses sub-categories"
msgstr "Restreindre à ces sous-categories"
-#: models.py:1340
+#: models.py:1380
msgid "If no sub-category is set all sub-categories are available"
msgstr ""
"Si aucune sous-catégorie n'est définie toutes les sous-catégories sont "
"disponibles"
-#: models.py:1342
+#: models.py:1382
msgid "Link to an external CSS"
msgstr "Lien vers une feuille de style externe"
-#: models.py:1344
+#: models.py:1386
+msgid "Public can read the map"
+msgstr "Carte lisible publiquement"
+
+#: models.py:1387
+msgid "Public can propose item to the map"
+msgstr "Des propositions de modification peuvent être faites publiquement"
+
+#: models.py:1388
+msgid "Public can write without moderation to the map"
+msgstr "Des modifications peuvent être faites publiquement"
+
+#: models.py:1389
msgid "Restrict to the area extent"
msgstr "Restreindre à l'étendue de la zone"
-#: models.py:1480 widgets.py:88
+#: models.py:1398 templates/chimere/blocks/footer.html:2
+msgid "Map"
+msgstr "Carte"
+
+#: models.py:1561
+msgid "Can read the map"
+msgstr "Peux lire la carte"
+
+#: models.py:1562
+msgid "Can propose item to the map"
+msgstr "Peux proposer des éléments sur la carte"
+
+#: models.py:1563
+msgid "Can write without moderation to the map"
+msgstr "Peux écrire sans modération sous la carte"
+
+#: models.py:1565
+msgid "Map - user"
+msgstr "Carte - Utilisateur"
+
+#: models.py:1566
+msgid "Map - users"
+msgstr "Cartes - Utilisateurs"
+
+#: models.py:1572 widgets.py:89
msgid "Default layer"
msgstr "Couche par défaut"
-#: models.py:1484 models.py:1485
+#: models.py:1576 models.py:1577
msgid "Layers"
msgstr "Couches"
-#: models.py:1493
+#: models.py:1585
msgid "Mandatory"
msgstr "Obligatoire"
-#: models.py:1496
+#: models.py:1588
msgid ""
"If no sub-category is set all the property applies to all sub-categories"
msgstr ""
"Si aucune sous-catégorie n'est précisée, cette propriété est disponible pour "
"toutes les sous-catégories"
-#: models.py:1498
+#: models.py:1590
msgid "Text"
msgstr "Texte"
-#: models.py:1499
+#: models.py:1591
msgid "Long text"
msgstr "Texte long"
-#: models.py:1502
+#: models.py:1594
msgid "Choices"
msgstr "Choix"
-#: models.py:1510
+#: models.py:1602
msgid "Type"
msgstr "Type"
-#: models.py:1515 models.py:1531 models.py:1545
+#: models.py:1607 models.py:1623 models.py:1637
msgid "Property model"
msgstr "Modèle de propriété"
-#: models.py:1532 models.py:1546
+#: models.py:1624 models.py:1638
msgid "Value"
msgstr "Valeur"
-#: models.py:1538
+#: models.py:1630
msgid "Model property choice"
msgstr "Choix pour les modèles de propriété"
-#: models.py:1557
+#: models.py:1649
msgid "Property"
msgstr "Propriété"
+#: settings.sample.py:84
+msgid "Foot"
+msgstr "À pied"
+
+#: settings.sample.py:85
+msgid "Bicycle"
+msgstr "À vélo"
+
+#: settings.sample.py:86
+msgid "Motorcar"
+msgstr "En voiture"
+
+#: settings.sample.py:89
+msgid "You are walking slowly"
+msgstr "Vous marchez lentement"
+
+#: settings.sample.py:90
+msgid "You are walking pretty quickly"
+msgstr "Vous marchez assez rapidement"
+
+#: settings.sample.py:91
+msgid "You are riding pretty slowly"
+msgstr "Vous roulez plutôt lentement"
+
+#: settings.sample.py:92
+msgid "You are riding pretty quickly"
+msgstr "Vous roulez plutôt rapidement"
+
#: tasks.py:63
msgid "Import pending"
msgstr "Import en attente"
@@ -865,10 +937,11 @@ msgstr "Le SRID n'a pu être trouvé. Le SRID par défaut (%s) a été utilisé.
#: utils.py:367
#, python-format
-msgid "Type of geographic item (%s) of this shapefile is not managed by Chimère."
+msgid ""
+"Type of geographic item (%s) of this shapefile is not managed by Chimère."
msgstr ""
-"Les types des éléments géographiques (%s) de ce fichier Shapefile ne sont pas "
- "gérés par Chimère."
+"Les types des éléments géographiques (%s) de ce fichier Shapefile ne sont "
+"pas gérés par Chimère."
#: utils.py:387
msgid "Bad Shapefile"
@@ -884,7 +957,7 @@ msgstr "Ne peut pas créer un champ"
#: utils.py:486 templates/admin/chimere/managed_modified.html:25
#: templates/chimere/edit.html:45 templates/chimere/edit_route.html:42
-#: templates/chimere/main_map.html:13
+#: templates/chimere/main_map.html:26
#: templates/chimere/main_map_simple.html:10
msgid "Categories"
msgstr "Catégories"
@@ -893,19 +966,19 @@ msgstr "Catégories"
msgid "Invalid CSV format"
msgstr "Fichier CSV non valide"
-#: utils.py:597
+#: utils.py:603
msgid "RSS feed is not well formed"
msgstr "Flux RSS non valide"
-#: utils.py:673
+#: utils.py:679
msgid "Nothing to import"
msgstr "Rien à importer"
-#: utils.py:757
+#: utils.py:763
msgid "New items imported - validate them before exporting"
msgstr "Nouveaux éléments importés - valider ceux-ci avant d'exporter"
-#: utils.py:759
+#: utils.py:765
msgid ""
"There are items from a former import not yet validated - validate them "
"before exporting"
@@ -913,19 +986,19 @@ msgstr ""
"Il y a des éléments d'un import précédent pas encore validé - Validez les "
"avant d'exporter"
-#: utils.py:771
+#: utils.py:777
msgid "Bad params - programming error"
msgstr "Mauvais paramètres - erreur de programmation"
-#: utils.py:781
+#: utils.py:787
msgid "Bad param"
msgstr "Mauvais paramètre"
-#: utils.py:796
+#: utils.py:802
msgid "No non ambigious tag is defined in the XAPI request"
msgstr "Pas de tag non ambigü définis dans la requête XAPI"
-#: utils.py:798
+#: utils.py:804
msgid ""
"No bounding box is defined in the XAPI request.If you are sure to manage the "
"entire planet set the bounding box to -180,-90,180,90"
@@ -934,19 +1007,23 @@ msgstr ""
"vouloir lancer la requête sur la planète entière fixez la « bounding box » "
"à -180,-90,180,90"
-#: views.py:290
+#: views.py:170
+msgid "Invalid user or password."
+msgstr "Nom d'utilisateur ou mot de passe incorrect"
+
+#: views.py:325
msgid "There are missing field(s) and/or errors in the submited form."
msgstr "Il y a des champs manquants ou des erreurs dans ce formulaire."
-#: views.py:375
+#: views.py:411
msgid "Bad file. Please check it with an external software."
msgstr "Fichier incohérent. Merci de le vérifier avec un logiciel externe."
-#: views.py:487
+#: views.py:524
msgid "Comments/request on the map"
msgstr "Commentaires/requètes sur la carte"
-#: views.py:490
+#: views.py:527
msgid ""
"Thank you for your contribution. It will be taken into account. If you have "
"left your email you may be contacted soon for more details."
@@ -955,56 +1032,56 @@ msgstr ""
"laissé votre courriel vous serez peut-être contacté bientôt pour plus de "
"détails."
-#: views.py:494
+#: views.py:531
msgid "Temporary error. Renew your message later."
msgstr "Erreur temporaire. Réenvoyez votre message plus tard."
-#: views.py:663
+#: views.py:725
msgid "No category available in this area."
msgstr "Pas de catégorie disponible sur cette zone."
-#: views.py:763
+#: views.py:825
msgid "Bad geometry"
msgstr "Géométrie incorrecte"
-#: views.py:848
+#: views.py:914
msgid "Incorrect choice in the list"
msgstr "Choix incorrect dans la liste"
-#: widgets.py:242
+#: widgets.py:243
msgid "Street, City, Country"
msgstr "Rue, Commune, Pays"
-#: widgets.py:311
+#: widgets.py:284
msgid "Latitude"
msgstr "Latitude"
-#: widgets.py:311
+#: widgets.py:286
msgid "Longitude"
msgstr "Longitude"
-#: widgets.py:335
+#: widgets.py:314
msgid "Invalid point"
msgstr "Point invalide"
-#: widgets.py:391
+#: widgets.py:371
msgid "Creation mode"
msgstr "Mode création"
-#: widgets.py:392
+#: widgets.py:372
msgid "To start drawing the route click on the toggle button: \"Draw\"."
msgstr ""
"Pour commencer le dessin cliquez sur le bouton : « Tracer »."
-#: widgets.py:394
+#: widgets.py:374
msgid "Then click on the map to begin the drawing."
msgstr "Puis cliquez sur la carte pour commencer le dessin."
-#: widgets.py:395
+#: widgets.py:375
msgid "You can add points by clicking again."
msgstr "Vous pouvez ajouter des points en cliquant de nouveau."
-#: widgets.py:396
+#: widgets.py:376
msgid ""
"To finish the drawing double click. When the drawing is finished you can "
"edit it."
@@ -1012,7 +1089,7 @@ msgstr ""
"Pour finir le tracé double-cliquez. Quand le tracé est fini vous pouvez "
"toujours l'éditer."
-#: widgets.py:398
+#: widgets.py:378
msgid ""
"While creating to undo a drawing click again on the toggle button \"Stop "
"drawing\"."
@@ -1020,17 +1097,17 @@ msgstr ""
"En mode création vous pouvez annuler un tracé en appuyant sur le bouton "
"« Arrêter le tracé »."
-#: widgets.py:403
+#: widgets.py:383
msgid "Modification mode"
msgstr "Mode modification"
-#: widgets.py:404
+#: widgets.py:384
msgid "To move a point click on it and drag it to the desired position."
msgstr ""
"Pour bouger un point, cliquez dessus, maintenez le click pour le déposer à "
"la position désirée."
-#: widgets.py:405
+#: widgets.py:385
msgid ""
"To delete a point move the mouse cursor over it and press the \"d\" or \"Del"
"\" key."
@@ -1038,7 +1115,7 @@ msgstr ""
"Pour supprimer un point, mettez le curseur de la souris sur celui-ci et "
"appuyez sur le touche « d » ou « Suppr »."
-#: widgets.py:407
+#: widgets.py:387
msgid ""
"To add a point click in the middle of a segment and drag the new point to "
"the desired position"
@@ -1047,51 +1124,51 @@ msgstr ""
"maintenez le bouton appuyé et déplacez le nouveau point à la position "
"désirée."
-#: widgets.py:414
+#: widgets.py:394
msgid "Give a name and set category before uploading a file."
msgstr ""
"Renseignez le nom et choisissez au moins une catégorie avant de déposer un "
"fichier."
-#: widgets.py:417
+#: widgets.py:397
msgid "Upload a route file (GPX or KML)"
msgstr "Déposer un trajet (fichier GPX ou KML)"
-#: widgets.py:418
+#: widgets.py:398
msgid "or"
msgstr "ou"
-#: widgets.py:423
+#: widgets.py:403
msgid "Start \"hand\" drawing"
msgstr "Commencer le tracé manuellement"
-#: widgets.py:446
+#: widgets.py:426
msgid "Move on the map"
msgstr "Se déplacer"
-#: widgets.py:446
+#: widgets.py:426
msgid "Draw"
msgstr "Tracer"
-#: widgets.py:536
+#: widgets.py:516
msgid "Hold CTRL, click and drag to select area on the map"
msgstr ""
"Maintenir la touche Control, cliquez puis glissez pour sélectionner une zone "
"sur la carte"
-#: widgets.py:593
+#: widgets.py:573
msgid "Type:"
msgstr "Type :"
-#: widgets.py:593
+#: widgets.py:573
msgid "Node"
msgstr "Nœud"
-#: widgets.py:594
+#: widgets.py:574
msgid "Way"
msgstr "Route"
-#: widgets.py:605
+#: widgets.py:585
msgid ""
"Enter an OSM \"tag=value\" string such as \"amenity=pub\". A list of common "
"tag is available ici ."
-#: widgets.py:612
+#: widgets.py:592
msgid "Tag:"
msgstr "Clé/valeur :"
-#: widgets.py:616
+#: widgets.py:596
msgid "You have to select an area."
msgstr "Vous devez sélectionner une zone."
-#: widgets.py:618
+#: widgets.py:598
msgid "You have to select a type."
msgstr "Vous devez sélectionner un type."
-#: widgets.py:620
+#: widgets.py:600
msgid "You have to insert a filter tag."
msgstr "Vous devez saisir une clé=valeur."
-#: widgets.py:622
+#: widgets.py:602
msgid "If you change the above form don't forget to refresh before submit!"
msgstr ""
"Si vous modifiez le formulaire ci-dessus n'oubliez pas de rafraîchir avant "
"de valider !"
-#: widgets.py:625
+#: widgets.py:605
msgid "You can put a Folder name of the KML file to filter on it."
msgstr ""
"Vous pouvez saisir le nom d'un « Folder » du fichier KML pour filter sur "
"celui-ci."
-#: widgets.py:633
+#: widgets.py:613
msgid "Refresh"
msgstr "Rafraîchir"
-#: widgets.py:699
+#: widgets.py:679
msgid "Select..."
msgstr "Sélectionner..."
@@ -1260,15 +1337,19 @@ msgstr "Licence :"
msgid "Show multimedia gallery"
msgstr "Montrer la galerie multimedia"
-#: templates/chimere/detail.html:32
+#: templates/chimere/detail.html:33
+msgid "Edit"
+msgstr "Modifier"
+
+#: templates/chimere/detail.html:34
msgid "Submit an amendment"
msgstr "Proposer une modification"
-#: templates/chimere/detail.html:35 templates/chimere/detail.html.py:36
+#: templates/chimere/detail.html:38 templates/chimere/detail.html.py:39
msgid "Propose amendment"
msgstr "Proposer une modification"
-#: templates/chimere/detail.html:35
+#: templates/chimere/detail.html:38
msgid "I would like to propose an amendment for this item:"
msgstr "Je souhaiterais proposer une modification pour cet élément :"
@@ -1345,10 +1426,30 @@ msgstr "Modifier un trajet"
msgid "Add a route"
msgstr "Ajout d'un nouveau trajet"
-#: templates/chimere/main_map.html:35
+#: templates/chimere/main_map.html:22 templates/chimere/no_map.html:20
+msgid "Identified as: "
+msgstr "Identifié en tant que : "
+
+#: templates/chimere/main_map.html:22 templates/chimere/no_map.html:20
+msgid "Logout"
+msgstr "Se déconnecter"
+
+#: templates/chimere/main_map.html:48
msgid "Simple map"
msgstr "Carte simple"
+#: templates/chimere/no_map.html:30
+msgid "No map are currently available for your account."
+msgstr "Aucune carte n'est disponible actuellement pour votre compte."
+
+#: templates/chimere/no_map.html:32
+msgid ""
+"No map are currently available for public access. If you have an account "
+"identify yourself."
+msgstr ""
+"Aucune carte n'est disponible actuellement en accès public. Si vous "
+"avez un compte identifiez vous."
+
#: templates/chimere/upload_file.html:13
msgid "Thank you for your submission!"
msgstr "Merci pour votre proposition !"
@@ -1409,17 +1510,13 @@ msgstr "Vous devez renseigner un fichier ou une adresse web."
msgid "You must provide a web address."
msgstr "Vous devez fournir une adresse web."
-#: templates/chimere/blocks/areas.html:4
-msgid "Areas:"
-msgstr "Zones :"
-
-#: templates/chimere/blocks/areas_alternative.html:4
-msgid "Shortcuts"
-msgstr "Raccourcis"
+#: templates/chimere/blocks/areas.html:5 templates/chimere/blocks/maps.html:5
+msgid "Maps:"
+msgstr "Cartes :"
-#: templates/chimere/blocks/areas_alternative.html:7
#: templates/chimere/blocks/categories.html:8
#: templates/chimere/blocks/categories.html:17
+#: templates/chimere/blocks/maps_alternative.html:7
msgid "Zoom to"
msgstr "Zoomer sur"
@@ -1436,10 +1533,6 @@ msgstr ""
msgid "This site uses Chimère"
msgstr "Ce site utilise Chimère"
-#: templates/chimere/blocks/footer.html:2
-msgid "Map"
-msgstr "Carte"
-
#: templates/chimere/blocks/map.html:9
msgid "Loading of the map in progress"
msgstr "Chargement de la carte en cours"
@@ -1452,7 +1545,7 @@ msgstr "Options d'affichage"
msgid "Map type"
msgstr "Type de carte"
-#: templates/chimere/blocks/map.html:24
+#: templates/chimere/blocks/map.html:25
msgid "Permalink"
msgstr "Lien permanent"
@@ -1488,6 +1581,10 @@ msgstr "Zoomer en arrière"
msgid "Center the map here"
msgstr "Centrer la carte ici"
+#: templates/chimere/blocks/maps_alternative.html:4
+msgid "Shortcuts"
+msgstr "Raccourcis"
+
#: templates/chimere/blocks/multimedia_file.html:19
msgid "Please use a modern browser or install the non free Flash-Plugin."
msgstr ""
@@ -1592,8 +1689,8 @@ msgid "Choose a category"
msgstr "Choisir une catégorie"
#: templates/chimere/feeds/rss.html:51
-msgid "Choose a pre-defined areas"
-msgstr "Choisir une zone pré-définie"
+msgid "Choose a pre-defined map"
+msgstr "Choisir une carte pré-définie"
#: templates/chimere/feeds/rss.html:65
msgid "Or select the area by zooming and panning this map"
@@ -1612,6 +1709,12 @@ msgstr " :"
msgid "Welcome to the %s"
msgstr "Bienvenue sur %s"
+#~ msgid "Associated areas"
+#~ msgstr "Zones associées"
+
+#~ msgid "Areas:"
+#~ msgstr "Zones :"
+
#~ msgid "Advanced options"
#~ msgstr "Options avancées"
--
cgit v1.2.3
From afdee58bb7db1928f1909d1fd31d55b1af8e1616 Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Wed, 19 Jun 2013 22:39:30 +0000
Subject: Fix urls for production
---
chimere/admin.py | 2 +-
chimere/templates/chimere/no_map.html | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/chimere/admin.py b/chimere/admin.py
index 67e0e51..80448f3 100644
--- a/chimere/admin.py
+++ b/chimere/admin.py
@@ -249,7 +249,7 @@ class MarkerAdmin(admin.ModelAdmin):
return managed_modified(self, request,
Marker.objects.filter(pk=item_id))
def get_urls(self):
- from django.conf.urls.defaults import *
+ from django.conf.urls.defaults import patterns, url
urls = super(MarkerAdmin, self).get_urls()
my_urls = patterns('',
url(r'^admin_modification/(?P\d+)/$',
diff --git a/chimere/templates/chimere/no_map.html b/chimere/templates/chimere/no_map.html
index c707a0b..a078e5e 100644
--- a/chimere/templates/chimere/no_map.html
+++ b/chimere/templates/chimere/no_map.html
@@ -1,5 +1,6 @@
{% extends "chimere/base.html" %}
{% load i18n unlocalize_point chimere_tags %}
+{% load url from future %}
{% block extra_head %}
{% head_jquery %}
{% head_chimere %}
--
cgit v1.2.3
From 4724e61c72e8c8122ec7b48546cd7dfb8c4f19d5 Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Mon, 1 Jul 2013 19:14:31 +0200
Subject: Templates: specify the "edit" class for edition pages
---
chimere/templates/chimere/edit.html | 2 ++
chimere/templates/chimere/edit_route.html | 2 ++
2 files changed, 4 insertions(+)
diff --git a/chimere/templates/chimere/edit.html b/chimere/templates/chimere/edit.html
index 509e740..d4e07c3 100644
--- a/chimere/templates/chimere/edit.html
+++ b/chimere/templates/chimere/edit.html
@@ -13,6 +13,8 @@
{% endif %}
{% endblock %}
+{% block body_class %}class='edit'{% endblock %}
+
{% block content %}
{{ block.super }}
{% if submited %}{% submited %}{% endif %}
diff --git a/chimere/templates/chimere/edit_route.html b/chimere/templates/chimere/edit_route.html
index 10f4d5e..15464c5 100644
--- a/chimere/templates/chimere/edit_route.html
+++ b/chimere/templates/chimere/edit_route.html
@@ -13,6 +13,8 @@
{% endif %}
{% endblock %}
+{% block body_class %}class='edit'{% endblock %}
+
{% block content %}
{{ block.super }}
{% if submited %}{% submited %}{% endif %}
--
cgit v1.2.3
From bb339ac51e4e7ef9548b8d9ffa14130586183aab Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Mon, 1 Jul 2013 19:43:19 +0200
Subject: Hide personnal info when authenticated - minor change on style
---
chimere/static/chimere/css/styles.css | 5 +++++
chimere/templates/chimere/edit.html | 6 ++++--
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/chimere/static/chimere/css/styles.css b/chimere/static/chimere/css/styles.css
index 28ced33..22138ae 100644
--- a/chimere/static/chimere/css/styles.css
+++ b/chimere/static/chimere/css/styles.css
@@ -735,6 +735,11 @@ p.warning{
padding:0.5em;
}
+p.submit{
+ border-top-width:1px;
+ text-align:center;
+}
+
#no-js-message{
z-index:5000;
text-align:center;
diff --git a/chimere/templates/chimere/edit.html b/chimere/templates/chimere/edit.html
index d4e07c3..9969ed5 100644
--- a/chimere/templates/chimere/edit.html
+++ b/chimere/templates/chimere/edit.html
@@ -26,6 +26,7 @@
{% if is_modification %}{% trans "Modify a point of interest" %}{% else %}{% trans "Add a point of interest" %}{% endif %}
{% endif %}
{% if is_modification and is_superuser %}{% trans "You are logged as an administrator. Your modifications will be taking into account immediately." %}
{% endif %}
{% if is_modification %}{% trans "Modify a point of interest" %}{% else %}{% trans "Add a point of interest" %}{% endif %}
@@ -56,13 +56,13 @@
{{ form.description.help_text }}
{% if dated %}
-
+
{% trans "Start date" %}
{{ form.start_date.errors }}
{{ form.start_date }}
{{ form.start_date.help_text }}
-
+
{% trans "End date" %}
{{ form.end_date.errors }}
{{ form.end_date }}
@@ -97,7 +97,7 @@
cat_filters['{{subcat.id}}'].push('#id_{{property.getNamedId}}');
{% endfor %}
{% endfor %}
- $('#id_categories').change(function(){
+ function checkFilteredProperties(){
for (idx in cat_to_hide){
$(cat_to_hide[idx]).parent().hide();
}
@@ -108,7 +108,8 @@
$(cat_filters[val][ids]).parent().show();
}
}
- });
+ }
+ $('#id_categories').change(checkFilteredProperties);
$('#id_categories').change();
{% endif %}
@@ -140,22 +141,42 @@
-
{% endblock %}
diff --git a/chimere/templates/chimere/edit_route.html b/chimere/templates/chimere/edit_route.html
index 15464c5..4dbb0d4 100644
--- a/chimere/templates/chimere/edit_route.html
+++ b/chimere/templates/chimere/edit_route.html
@@ -51,13 +51,13 @@
{{ form.description }}
{% if dated %}
-
+
{% trans "Start date" %}
{{ form.start_date.errors }}
{{ form.start_date }}
{{ form.start_date.help_text }}
-
+
--
cgit v1.2.3
From 2d5e5bef57c3f16b442ed18e19d36c0e611a29fe Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Sun, 7 Jul 2013 19:09:16 +0200
Subject: Minor update on french translation
---
chimere/locale/fr/LC_MESSAGES/django.po | 110 ++++++++++++++++----------------
chimere/models.py | 2 +-
2 files changed, 57 insertions(+), 55 deletions(-)
diff --git a/chimere/locale/fr/LC_MESSAGES/django.po b/chimere/locale/fr/LC_MESSAGES/django.po
index 13ca044..38fca68 100644
--- a/chimere/locale/fr/LC_MESSAGES/django.po
+++ b/chimere/locale/fr/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.2\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2013-06-18 19:06+0200\n"
+"POT-Creation-Date: 2013-07-07 19:08+0200\n"
"PO-Revision-Date: 2010-03-20 20:00+0100\n"
"Last-Translator: Étienne Loks \n"
"MIME-Version: 1.0\n"
@@ -219,46 +219,46 @@ msgstr "La date de fin ne peut pas être antérieure à la date de début"
msgid "This field is mandatory for the selected categories"
msgstr "Ce champ est obligatoire pour les catégories sélectionnées"
-#: forms.py:501
+#: forms.py:507
msgid "File"
msgstr "Fichier"
-#: forms.py:507
+#: forms.py:513
msgid "Bad file format: this must be a GPX or KML file"
msgstr "Mauvais format de fichier : KML et GPX sont supportés"
-#: forms.py:512 models.py:53 models.py:101 models.py:163 models.py:185
+#: forms.py:518 models.py:53 models.py:101 models.py:163 models.py:185
#: models.py:198 models.py:213 models.py:378 models.py:752 models.py:808
#: models.py:867 models.py:985 models.py:1343 models.py:1355 models.py:1582
#: utils.py:485 templates/admin/chimere/managed_modified.html:23
-#: templates/chimere/edit.html:40 templates/chimere/edit_route.html:37
+#: templates/chimere/edit.html:43 templates/chimere/edit_route.html:39
#: templates/chimere/blocks/alternate_multimedia.html:39
msgid "Name"
msgstr "Nom"
-#: forms.py:521
+#: forms.py:527
msgid "Area"
msgstr "Zone"
-#: forms.py:561
+#: forms.py:567
msgid "No area selected."
msgstr "Pas de zone sélectionnée."
-#: forms.py:568
+#: forms.py:574
#, python-format
msgid "The area \"%s\" has the same order, you need to choose another one."
msgstr ""
"La zone « %s » a le même numéro d'ordre, vous devez un choisir un autre."
-#: forms.py:619
+#: forms.py:625
msgid "Start"
msgstr "Départ"
-#: forms.py:620
+#: forms.py:626
msgid "Finish"
msgstr "Arrivée"
-#: forms.py:621
+#: forms.py:627
msgid "Speed"
msgstr "Vitesse"
@@ -359,7 +359,7 @@ msgid "Marker"
msgstr "Point d'intérêt"
#: models.py:220 models.py:1047 models.py:1064
-#: templates/chimere/edit_route.html:28
+#: templates/chimere/edit_route.html:30
msgid "Route"
msgstr "Trajet"
@@ -515,8 +515,8 @@ msgstr "Modifié depuis le dernier import"
msgid "Not to be exported to OSM"
msgstr "À ne pas exporter vers OSM"
-#: models.py:403 templates/chimere/edit.html:57
-#: templates/chimere/edit_route.html:53
+#: models.py:403 templates/chimere/edit.html:60
+#: templates/chimere/edit_route.html:55
msgid "Start date"
msgstr "Date de début"
@@ -526,8 +526,8 @@ msgstr ""
"Optionnel. Précisez ce champ pour les éléments datés comme un événement. "
"Format du champ : AAAA-MM-JJ"
-#: models.py:406 templates/chimere/edit.html:63
-#: templates/chimere/edit_route.html:59
+#: models.py:406 templates/chimere/edit.html:66
+#: templates/chimere/edit_route.html:61
msgid "End date"
msgstr "Date de fin"
@@ -555,7 +555,7 @@ msgid "Quantity"
msgstr "Quantité"
#: models.py:473 utils.py:490 templates/admin/chimere/managed_modified.html:31
-#: templates/chimere/edit.html:50 templates/chimere/edit_route.html:47
+#: templates/chimere/edit.html:53 templates/chimere/edit_route.html:49
msgid "Description"
msgstr "Description"
@@ -956,7 +956,7 @@ msgid "Failed to create field"
msgstr "Ne peut pas créer un champ"
#: utils.py:486 templates/admin/chimere/managed_modified.html:25
-#: templates/chimere/edit.html:45 templates/chimere/edit_route.html:42
+#: templates/chimere/edit.html:48 templates/chimere/edit_route.html:44
#: templates/chimere/main_map.html:26
#: templates/chimere/main_map_simple.html:10
msgid "Categories"
@@ -1011,19 +1011,19 @@ msgstr ""
msgid "Invalid user or password."
msgstr "Nom d'utilisateur ou mot de passe incorrect"
-#: views.py:325
+#: views.py:326
msgid "There are missing field(s) and/or errors in the submited form."
msgstr "Il y a des champs manquants ou des erreurs dans ce formulaire."
-#: views.py:411
+#: views.py:412
msgid "Bad file. Please check it with an external software."
msgstr "Fichier incohérent. Merci de le vérifier avec un logiciel externe."
-#: views.py:524
+#: views.py:525
msgid "Comments/request on the map"
msgstr "Commentaires/requètes sur la carte"
-#: views.py:527
+#: views.py:528
msgid ""
"Thank you for your contribution. It will be taken into account. If you have "
"left your email you may be contacted soon for more details."
@@ -1032,19 +1032,19 @@ msgstr ""
"laissé votre courriel vous serez peut-être contacté bientôt pour plus de "
"détails."
-#: views.py:531
+#: views.py:532
msgid "Temporary error. Renew your message later."
msgstr "Erreur temporaire. Réenvoyez votre message plus tard."
-#: views.py:725
+#: views.py:726
msgid "No category available in this area."
msgstr "Pas de catégorie disponible sur cette zone."
-#: views.py:825
+#: views.py:826
msgid "Bad geometry"
msgstr "Géométrie incorrecte"
-#: views.py:914
+#: views.py:915
msgid "Incorrect choice in the list"
msgstr "Choix incorrect dans la liste"
@@ -1353,11 +1353,11 @@ msgstr "Proposer une modification"
msgid "I would like to propose an amendment for this item:"
msgstr "Je souhaiterais proposer une modification pour cet élément :"
-#: templates/chimere/edit.html:20
+#: templates/chimere/edit.html:22
msgid "Error"
msgstr "Erreur"
-#: templates/chimere/edit.html:23 templates/chimere/edit_route.html:20
+#: templates/chimere/edit.html:25 templates/chimere/edit_route.html:22
msgid ""
"You are logged as an administrator. Your modifications will be taking into "
"account immediately."
@@ -1365,31 +1365,40 @@ msgstr ""
"Vous êtes connecté comme administrateur. Vos modifications vont être prises "
"en compte immédiatement."
-#: templates/chimere/edit.html:25
+#: templates/chimere/edit.html:27
msgid "Modify a point of interest"
msgstr "Modifier un point d'intérêt"
-#: templates/chimere/edit.html:25
+#: templates/chimere/edit.html:27
msgid "Add a point of interest"
msgstr "Ajout d'un point d'intérêt"
-#: templates/chimere/edit.html:31
+#: templates/chimere/edit.html:29 templates/chimere/edit.html.py:179
+msgid "Add/modify"
+msgstr "Ajouter - Modifier"
+
+#: templates/chimere/edit.html:29 templates/chimere/edit.html.py:179
+#: templates/chimere/edit_route.html:103
+msgid "Propose"
+msgstr "Proposez"
+
+#: templates/chimere/edit.html:34
msgid "Point"
msgstr "Point"
-#: templates/chimere/edit.html:32 templates/chimere/edit_route.html:29
+#: templates/chimere/edit.html:35 templates/chimere/edit_route.html:31
msgid "Select a location for this new site"
msgstr "Choisissez une localisation pour ce nouveau site"
-#: templates/chimere/edit.html:38 templates/chimere/edit_route.html:35
+#: templates/chimere/edit.html:41 templates/chimere/edit_route.html:37
msgid "indicates a mandatory field"
msgstr "indique un champ obligatoire"
-#: templates/chimere/edit.html:114
+#: templates/chimere/edit.html:119
msgid "Personal information"
msgstr "Informations personnelles"
-#: templates/chimere/edit.html:116
+#: templates/chimere/edit.html:121
msgid ""
"This fields are not mandatory. If you provided them they not will be made "
"public and they will only used to join you for this project."
@@ -1398,39 +1407,35 @@ msgstr ""
"pas publiés et ne seront utilisés seulement pour vous joindre dans le cadre "
"de ce projet."
-#: templates/chimere/edit.html:119
+#: templates/chimere/edit.html:124
msgid "Your name or nickname"
msgstr "Votre nom ou pseudo"
-#: templates/chimere/edit.html:124
+#: templates/chimere/edit.html:129
msgid "Your email"
msgstr "Votre courriel"
-#: templates/chimere/edit.html:129
+#: templates/chimere/edit.html:134
msgid "Comments about your submission"
msgstr "Commentaires au sujet de votre proposition"
-#: templates/chimere/edit.html:135
+#: templates/chimere/edit.html:140
msgid "Upload in progress. Please wait..."
msgstr "Dépôt en cours. Veuillez patienter..."
-#: templates/chimere/edit.html:153 templates/chimere/edit_route.html:78
-msgid "Propose"
-msgstr "Proposez"
-
-#: templates/chimere/edit_route.html:22
+#: templates/chimere/edit_route.html:24
msgid "Modify a route"
msgstr "Modifier un trajet"
-#: templates/chimere/edit_route.html:22
+#: templates/chimere/edit_route.html:24
msgid "Add a route"
msgstr "Ajout d'un nouveau trajet"
-#: templates/chimere/main_map.html:22 templates/chimere/no_map.html:20
+#: templates/chimere/main_map.html:22 templates/chimere/no_map.html:21
msgid "Identified as: "
msgstr "Identifié en tant que : "
-#: templates/chimere/main_map.html:22 templates/chimere/no_map.html:20
+#: templates/chimere/main_map.html:22 templates/chimere/no_map.html:21
msgid "Logout"
msgstr "Se déconnecter"
@@ -1438,17 +1443,17 @@ msgstr "Se déconnecter"
msgid "Simple map"
msgstr "Carte simple"
-#: templates/chimere/no_map.html:30
+#: templates/chimere/no_map.html:31
msgid "No map are currently available for your account."
msgstr "Aucune carte n'est disponible actuellement pour votre compte."
-#: templates/chimere/no_map.html:32
+#: templates/chimere/no_map.html:33
msgid ""
"No map are currently available for public access. If you have an account "
"identify yourself."
msgstr ""
-"Aucune carte n'est disponible actuellement en accès public. Si vous "
-"avez un compte identifiez vous."
+"Aucune carte n'est disponible actuellement en accès public. Si vous avez un "
+"compte identifiez vous."
#: templates/chimere/upload_file.html:13
msgid "Thank you for your submission!"
@@ -1727,9 +1732,6 @@ msgstr "Bienvenue sur %s"
#~ msgid "Administration de Chimère"
#~ msgstr "Administration de Chimère"
-#~ msgid "Add/modify a site"
-#~ msgstr "Ajouter ou modifier un site"
-
#~ msgid "Categorys"
#~ msgstr "Catégories"
diff --git a/chimere/models.py b/chimere/models.py
index f851b9c..0591701 100644
--- a/chimere/models.py
+++ b/chimere/models.py
@@ -1534,7 +1534,7 @@ post_save.connect(map_post_save, sender=Map)
def get_maps_for_user(user):
"""
- Getting subcats for a specific user
+ Getting maps for a specific user
"""
perms = user.get_all_permissions()
maps = set()
--
cgit v1.2.3
From 42772dbf98a81aae92a17054d65a4b0c11783eb8 Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Mon, 5 Aug 2013 17:41:51 +0200
Subject: Fix duplicate on map list
---
chimere/models.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/chimere/models.py b/chimere/models.py
index 0591701..fa139e0 100644
--- a/chimere/models.py
+++ b/chimere/models.py
@@ -1422,7 +1422,7 @@ class Map(models.Model, SimpleArea):
query = Q(**fltr)
else:
query = query | Q(**fltr)
- maps = cls.objects.filter(query)
+ maps = cls.objects.filter(query).distinct()
if single:
if not maps.count():
return
--
cgit v1.2.3
From a50daee604b44c097a688f1ef94346ad3f804d41 Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Wed, 7 Aug 2013 13:19:07 +0200
Subject: Tests: refactoring Area -> Map
---
chimere/tests.py | 69 ++++++++++++++++++++++++++++++++++++--------------------
1 file changed, 44 insertions(+), 25 deletions(-)
diff --git a/chimere/tests.py b/chimere/tests.py
index 070443e..d5096b4 100644
--- a/chimere/tests.py
+++ b/chimere/tests.py
@@ -9,7 +9,7 @@ test_path = os.path.abspath(__file__)
test_dir_path = os.path.dirname(test_path) + os.sep
from django.conf import settings
-from django.contrib.auth.models import User
+from django.contrib.auth.models import User, Group
from django.contrib.gis.geos import GEOSGeometry
from django.contrib.messages.storage.fallback import FallbackStorage
from django.core.urlresolvers import reverse
@@ -18,27 +18,12 @@ from django.test import TestCase
from django.test.client import Client
from chimere.admin import managed_modified, MarkerAdmin
-from chimere.models import Area, Icon, Importer, Category, SubCategory, Marker,\
- Route, News
-from chimere.forms import MarkerForm, AreaAdminForm
+from chimere.models import Icon, Importer, Category, SubCategory, Marker,\
+ Route, News, Map, MapUsers, MapGroups
+from chimere.forms import MarkerForm, MapAdminForm
from chimere.templatetags.chimere_tags import display_news
from chimere.utils import ShapefileManager
-def areas_setup():
- area_1 = Area.objects.create(name='area 1', urn='area-1', order=1,
- available=True,
- upper_left_corner='SRID=4326;POINT(-4.907753 48.507656)',
- lower_right_corner='SRID=4326;POINT(-4.049447 48.279688)')
- area_2 = Area.objects.create(name='area 2', urn='area-2', order=2,
- available=True,
- upper_left_corner='SRID=4326;POINT(-3 47.5)',
- lower_right_corner='SRID=4326;POINT(-2.5 47)')
- area_3 = Area.objects.create(name='area 3', urn='area-3', order=3,
- available=True,
- upper_left_corner='SRID=4326;POINT(-1.5 1.5)',
- lower_right_corner='SRID=4326;POINT(1.5 -1.5)')
- return [area_1, area_2, area_3]
-
def subcategory_setup():
category = Category.objects.create(name='Main category',
available=True,
@@ -129,6 +114,40 @@ def route_setup(sub_categories=[]):
routes.append(route_2)
return routes
+def map_setup():
+ maps = []
+ maps.append(Map.objects.create(name='Map 1', available=True, order=1,
+ upper_left_corner='SRID=4326;POINT(-4.907753 48.507656)',
+ lower_right_corner='SRID=4326;POINT(-4.049447 48.279688)',
+ urn='map-1', default=False, public_read=True,
+ public_propose=True, public_write=False))
+ maps.append(Map.objects.create(name='Map 2', available=True, order=2,
+ upper_left_corner='SRID=4326;POINT(-3 47.5)',
+ lower_right_corner='SRID=4326;POINT(-2.5 47)',
+ urn='map-2', default=False, public_read=True,
+ public_propose=False, public_write=False))
+ maps.append(Map.objects.create(name='Map 3', available=True, order=3,
+ upper_left_corner='SRID=4326;POINT(-1.5 1.5)',
+ lower_right_corner='SRID=4326;POINT(1.5 -1.5)',
+ urn='map-3', default=False, public_read=False,
+ public_propose=False, public_write=False))
+ return maps
+
+def users_setup():
+ adminuser = User.objects.create_superuser('admin',
+ 'admin@test.com',
+ 'pass')
+ users = [adminuser]
+ users.append(User.objects.create_user('user1', 'user1@test.com',
+ 'pass'))
+ users.append(User.objects.create_user('user2', 'user2@test.com',
+ 'pass'))
+ groups = [Group.objects.create(name='groupname')]
+ users[2].groups.add(groups[0])
+ return users, groups
+
+areas_setup = users_setup
+
class ImporterTest:
def test_get(self):
nb_by_cat = {}
@@ -357,7 +376,7 @@ class MarkerFormTest(TestCase):
form = MarkerForm(data)
self.assertEqual(form.is_valid(), False)
-class AreaTest(TestCase):
+class MapTest(TestCase):
def setUp(self):
self.areas = areas_setup()
@@ -368,7 +387,7 @@ class AreaTest(TestCase):
response = self.client.get('/%s/' % area_1.urn)
self.assertRedirects(response, '/')
-class AreaAdminFormTest(TestCase):
+class MapAdminFormTest(TestCase):
def setUp(self):
self.areas = areas_setup()
@@ -378,7 +397,7 @@ class AreaAdminFormTest(TestCase):
area_1.save()
area_2.default = True
area_2.save()
- area_1 = Area.objects.get(urn=area_1.urn)
+ area_1 = Map.objects.get(urn=area_1.urn)
self.assertEqual(area_1.default, False)
def test_area_creation(self):
@@ -393,12 +412,12 @@ class AreaAdminFormTest(TestCase):
# order already given
data = base_data.copy()
data['order'] = self.areas[0].order
- form = AreaAdminForm(data)
+ form = MapAdminForm(data)
self.assertEqual(form.is_valid(), False)
# update an already existing area
data = base_data.copy()
data['order'] = self.areas[0].order
- form = AreaAdminForm(data, instance=self.areas[0])
+ form = MapAdminForm(data, instance=self.areas[0])
self.assertEqual(form.is_valid(), True)
# empty area
data = base_data.copy()
@@ -406,7 +425,7 @@ class AreaAdminFormTest(TestCase):
'upper_left_lon': 0,
'lower_right_lat': 0,
'lower_right_lon': 0})
- form = AreaAdminForm(data)
+ form = MapAdminForm(data)
self.assertEqual(form.is_valid(), False)
class DynamicCategoryTest(TestCase):
--
cgit v1.2.3
From ef399e463e9ad6537e17f8d7aa3a16bfc86e4544 Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Wed, 7 Aug 2013 13:20:12 +0200
Subject: Map: manage group permissions - fix some issues on permissions -
tests
---
chimere/admin.py | 8 +-
chimere/migrations/0008_auto__add_mapgroups.py | 345 +++++++++++++++++++++++++
chimere/models.py | 22 +-
chimere/tests.py | 40 +++
chimere/views.py | 2 +-
5 files changed, 410 insertions(+), 7 deletions(-)
create mode 100644 chimere/migrations/0008_auto__add_mapgroups.py
diff --git a/chimere/admin.py b/chimere/admin.py
index 80448f3..9f65707 100644
--- a/chimere/admin.py
+++ b/chimere/admin.py
@@ -42,7 +42,7 @@ from chimere.forms import MarkerAdminForm, RouteAdminForm, MapAdminForm,\
from chimere.models import Category, Icon, SubCategory, Marker, \
PropertyModel, News, Route, Map, ColorTheme, Color, \
MultimediaFile, PictureFile, Importer, Layer, MapLayers,\
- PropertyModelChoice, MultimediaExtension, Page, MapUsers,\
+ PropertyModelChoice, MultimediaExtension, Page, MapUsers, MapGroups,\
get_maps_for_user, get_users_by_map
from chimere.utils import unicode_normalize, ShapefileManager, KMLManager,\
CSVManager
@@ -321,13 +321,17 @@ class UserInline(admin.TabularInline):
model = MapUsers
extra = 1
+class GroupInline(admin.TabularInline):
+ model = MapGroups
+ extra = 1
+
class MapAdmin(admin.ModelAdmin):
"""
Specialized the map field.
"""
form = MapAdminForm
exclude = ['upper_left_corner', 'lower_right_corner']
- inlines = [UserInline, LayerInline]
+ inlines = [UserInline, GroupInline, LayerInline]
list_display = ['name', 'order', 'available', 'default']
def importing(modeladmin, request, queryset):
diff --git a/chimere/migrations/0008_auto__add_mapgroups.py b/chimere/migrations/0008_auto__add_mapgroups.py
new file mode 100644
index 0000000..273e3f6
--- /dev/null
+++ b/chimere/migrations/0008_auto__add_mapgroups.py
@@ -0,0 +1,345 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding model 'MapGroups'
+ db.create_table('chimere_mapgroups', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('map', self.gf('django.db.models.fields.related.ForeignKey')(related_name='mapgroups', to=orm['chimere.Map'])),
+ ('group', self.gf('django.db.models.fields.related.ForeignKey')(related_name='mapgroups', to=orm['auth.Group'])),
+ ('read', self.gf('django.db.models.fields.BooleanField')(default=False)),
+ ('propose', self.gf('django.db.models.fields.BooleanField')(default=False)),
+ ('write', self.gf('django.db.models.fields.BooleanField')(default=False)),
+ ))
+ db.send_create_signal('chimere', ['MapGroups'])
+
+
+ def backwards(self, orm):
+ # Deleting model 'MapGroups'
+ db.delete_table('chimere_mapgroups')
+
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'chimere.aggregatedroute': {
+ 'Meta': {'object_name': 'AggregatedRoute', 'db_table': "'chimere_aggregated_routes'", 'managed': 'False'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'route': ('django.contrib.gis.db.models.fields.MultiLineStringField', [], {}),
+ 'status': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'subcategory': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.SubCategory']"})
+ },
+ 'chimere.category': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'Category'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.color': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'Color'},
+ 'code': ('django.db.models.fields.CharField', [], {'max_length': '6'}),
+ 'color_theme': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'colors'", 'to': "orm['chimere.ColorTheme']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.colortheme': {
+ 'Meta': {'object_name': 'ColorTheme'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.icon': {
+ 'Meta': {'object_name': 'Icon'},
+ 'height': ('django.db.models.fields.IntegerField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'width': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.importer': {
+ 'Meta': {'object_name': 'Importer'},
+ 'associate_marker_to_way': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'categories': ('chimere.widgets.SelectMultipleField', [], {'to': "orm['chimere.SubCategory']", 'symmetrical': 'False'}),
+ 'default_description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'default_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'filtr': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'get_description': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'importer_type': ('django.db.models.fields.CharField', [], {'max_length': '4'}),
+ 'license': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'origin': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'overwrite': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'source': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'source_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'srid': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'state': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'zipped': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'chimere.layer': {
+ 'Meta': {'object_name': 'Layer'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layer_code': ('django.db.models.fields.TextField', [], {'max_length': '300'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.map': {
+ 'Meta': {'ordering': "('order', 'name')", 'object_name': 'Map'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'cluster': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'default': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'default_subcategories': ('chimere.widgets.SelectMultipleField', [], {'to': "orm['chimere.SubCategory']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'dynamic_categories': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'external_css': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layers': ('chimere.widgets.SelectMultipleField', [], {'symmetrical': 'False', 'related_name': "'maps'", 'blank': 'True', 'through': "orm['chimere.MapLayers']", 'to': "orm['chimere.Layer']"}),
+ 'lower_right_corner': ('django.contrib.gis.db.models.fields.PointField', [], {'default': "'POINT(0 0)'"}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'unique': 'True'}),
+ 'public_propose': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'public_read': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'public_write': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'restrict_to_extent': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'subcategories': ('chimere.widgets.SelectMultipleField', [], {'symmetrical': 'False', 'related_name': "'maps'", 'blank': 'True', 'db_table': "'chimere_subcategory_maps'", 'to': "orm['chimere.SubCategory']"}),
+ 'upper_left_corner': ('django.contrib.gis.db.models.fields.PointField', [], {'default': "'POINT(0 0)'"}),
+ 'urn': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'blank': 'True'}),
+ 'users': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'through': "orm['chimere.MapUsers']", 'symmetrical': 'False'}),
+ 'welcome_message': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.mapgroups': {
+ 'Meta': {'object_name': 'MapGroups'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'mapgroups'", 'to': "orm['auth.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'map': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'mapgroups'", 'to': "orm['chimere.Map']"}),
+ 'propose': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'read': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'write': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'chimere.maplayers': {
+ 'Meta': {'ordering': "('order',)", 'object_name': 'MapLayers'},
+ 'default': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.Layer']"}),
+ 'map': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.Map']"}),
+ 'order': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.mapusers': {
+ 'Meta': {'object_name': 'MapUsers'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'map': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'mapusers'", 'to': "orm['chimere.Map']"}),
+ 'propose': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'read': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'mapusers'", 'to': "orm['auth.User']"}),
+ 'write': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'chimere.marker': {
+ 'Meta': {'ordering': "('status', 'name')", 'object_name': 'Marker'},
+ 'available_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'categories': ('chimere.widgets.SelectMultipleField', [], {'to': "orm['chimere.SubCategory']", 'symmetrical': 'False'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'end_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'import_key': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'import_source': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'import_version': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'is_front_page': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'license': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'modified_since_import': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'not_for_osm': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'origin': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'point': ('chimere.widgets.PointField', [], {}),
+ 'ref_item': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'submited_marker'", 'null': 'True', 'to': "orm['chimere.Marker']"}),
+ 'route': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'associated_marker'", 'null': 'True', 'to': "orm['chimere.Route']"}),
+ 'start_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'status': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'submiter_comment': ('django.db.models.fields.TextField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'submiter_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
+ 'submiter_name': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
+ 'submiter_session_key': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
+ 'weight': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.multimediaextension': {
+ 'Meta': {'object_name': 'MultimediaExtension'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'multimedia_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'extensions'", 'to': "orm['chimere.MultimediaType']"}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '6'})
+ },
+ 'chimere.multimediafile': {
+ 'Meta': {'object_name': 'MultimediaFile'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'marker': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'multimedia_files'", 'to': "orm['chimere.Marker']"}),
+ 'miniature': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'multimedia_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.MultimediaType']", 'null': 'True', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'url': ('django.db.models.fields.URLField', [], {'max_length': '200'})
+ },
+ 'chimere.multimediatype': {
+ 'Meta': {'object_name': 'MultimediaType'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'iframe': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'media_type': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'mime_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.news': {
+ 'Meta': {'object_name': 'News'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'content': ('django.db.models.fields.TextField', [], {}),
+ 'date': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_front_page': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'maps': ('chimere.widgets.SelectMultipleField', [], {'symmetrical': 'False', 'to': "orm['chimere.Map']", 'null': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.page': {
+ 'Meta': {'object_name': 'Page'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'content': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mnemonic': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '10', 'null': 'True', 'blank': 'True'}),
+ 'template_path': ('django.db.models.fields.CharField', [], {'max_length': '150', 'null': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.picturefile': {
+ 'Meta': {'object_name': 'PictureFile'},
+ 'height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'marker': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'pictures'", 'to': "orm['chimere.Marker']"}),
+ 'miniature': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'picture': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
+ 'thumbnailfile': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'thumbnailfile_height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'thumbnailfile_width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.property': {
+ 'Meta': {'object_name': 'Property'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'marker': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.Marker']"}),
+ 'propertymodel': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.PropertyModel']"}),
+ 'value': ('django.db.models.fields.TextField', [], {})
+ },
+ 'chimere.propertymodel': {
+ 'Meta': {'ordering': "('order',)", 'object_name': 'PropertyModel'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mandatory': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {}),
+ 'subcategories': ('chimere.widgets.SelectMultipleField', [], {'symmetrical': 'False', 'related_name': "'properties'", 'blank': 'True', 'to': "orm['chimere.SubCategory']"}),
+ 'type': ('django.db.models.fields.CharField', [], {'max_length': '1'})
+ },
+ 'chimere.propertymodelchoice': {
+ 'Meta': {'object_name': 'PropertyModelChoice'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'propertymodel': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'choices'", 'to': "orm['chimere.PropertyModel']"}),
+ 'value': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.route': {
+ 'Meta': {'ordering': "('status', 'name')", 'object_name': 'Route'},
+ 'associated_file': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.RouteFile']", 'null': 'True', 'blank': 'True'}),
+ 'categories': ('chimere.widgets.SelectMultipleField', [], {'to': "orm['chimere.SubCategory']", 'symmetrical': 'False'}),
+ 'end_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'has_associated_marker': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'import_key': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'import_source': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'import_version': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'license': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'modified_since_import': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'not_for_osm': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'origin': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'picture': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'ref_item': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'submited_route'", 'null': 'True', 'to': "orm['chimere.Route']"}),
+ 'route': ('chimere.widgets.RouteField', [], {}),
+ 'start_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'status': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'submiter_comment': ('django.db.models.fields.TextField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'submiter_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
+ 'submiter_name': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
+ 'submiter_session_key': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
+ 'width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.routefile': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'RouteFile'},
+ 'file_type': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'raw_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+ 'simplified_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.subcategory': {
+ 'Meta': {'ordering': "['category', 'order']", 'object_name': 'SubCategory'},
+ 'as_layer': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'category': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'subcategories'", 'to': "orm['chimere.Category']"}),
+ 'color_theme': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.ColorTheme']", 'null': 'True', 'blank': 'True'}),
+ 'dated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'hover_icon': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'subcat_hovered'", 'null': 'True', 'to': "orm['chimere.Icon']"}),
+ 'icon': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.Icon']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'item_type': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '1000'}),
+ 'routing_warn': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'submission': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'weighted': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'chimere.tinyurl': {
+ 'Meta': {'object_name': 'TinyUrl'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'parameters': ('django.db.models.fields.CharField', [], {'max_length': '500'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['chimere']
\ No newline at end of file
diff --git a/chimere/models.py b/chimere/models.py
index fa139e0..cd2996d 100644
--- a/chimere/models.py
+++ b/chimere/models.py
@@ -1411,10 +1411,14 @@ class Map(models.Model, SimpleArea):
] if edit else [
{'public_read':True}]
if user and user.is_authenticated():
- filters += [{'mapusers__user':user, 'mapusers__write':True},
- {'mapusers__user':user, 'mapusers__propose':True},
- ] if edit else [
- {'mapusers__user':user, 'mapusers__read':True},]
+ filters += [
+ {'mapusers__user':user, 'mapusers__write':True},
+ {'mapusers__user':user, 'mapusers__propose':True},
+ {'mapgroups__group__user':user, 'mapgroups__write':True},
+ {'mapgroups__group__user':user, 'mapgroups__propose':True},
+ ] if edit else [
+ {'mapusers__user':user, 'mapusers__read':True},
+ {'mapgroups__group__user':user, 'mapgroups__read':True},]
query = None
for fltr in filters:
fltr.update(map_filter)
@@ -1565,6 +1569,16 @@ class MapUsers(models.Model):
verbose_name = _("Map - user")
verbose_name_plural = _("Map - users")
+class MapGroups(models.Model):
+ map = models.ForeignKey(Map, related_name='mapgroups')
+ group = models.ForeignKey(Group, related_name='mapgroups')
+ read = models.BooleanField(_(u"Can read the map"))
+ propose = models.BooleanField(_(u"Can propose item to the map"))
+ write = models.BooleanField(_(u"Can write without moderation to the map"))
+ class Meta:
+ verbose_name = _("Map - group")
+ verbose_name_plural = _("Map - groups")
+
class MapLayers(models.Model):
map = models.ForeignKey(Map)
layer = models.ForeignKey(Layer)
diff --git a/chimere/tests.py b/chimere/tests.py
index d5096b4..89c6ee3 100644
--- a/chimere/tests.py
+++ b/chimere/tests.py
@@ -559,3 +559,43 @@ class RouteTest(TestCase):
route='SRID=4326;LINESTRING (30 10, 10 30, 40 40)',
has_associated_marker=False)
self.assertEqual(Marker.objects.filter(route=route_2).count(), 0)
+
+class PermissionTest(TestCase):
+ def setUp(self):
+ self.maps = map_setup()
+ self.users, self.groups = users_setup()
+ MapUsers.objects.create(map=self.maps[2], user=self.users[1], read=True,
+ propose=False, write=False)
+ MapGroups.objects.create(map=self.maps[2], group=self.groups[0],
+ read=True, propose=True, write=True)
+
+ def test_rights(self):
+ self.client.login(username=self.users[0].username, password='pass')
+ for urn, can_view, can_propose in [('map-1', True, True),
+ ('map-2', True, False),
+ ('map-3', False, False)]:
+ self.check_right(urn, can_view, can_propose, self.users[0].username)
+ self.client.login(username=self.users[1].username, password='pass')
+ for urn, can_view, can_propose in [('map-1', True, True),
+ ('map-2', True, False),
+ ('map-3', True, False)]:
+ self.check_right(urn, can_view, can_propose, self.users[1].username)
+ self.client.login(username=self.users[2].username, password='pass')
+ for urn, can_view, can_propose in [('map-1', True, True),
+ ('map-2', True, False),
+ ('map-3', True, True)]:
+ self.check_right(urn, can_view, can_propose, self.users[2].username)
+
+ def check_right(self, urn, can_view, can_propose, username):
+ url = reverse('chimere:index', args=[urn])
+ response = self.client.get(url)
+ status_code = 200 if can_view else 302
+ self.assertEqual(status_code, response.status_code,
+ msg='index for %s, %s: %s got where %s is attended' % (
+ username, urn, response.status_code, status_code))
+ url = reverse('chimere:edit', args=[urn+'/'])
+ status_code = 200 if can_propose else 302
+ response = self.client.get(url)
+ self.assertEqual(status_code, response.status_code,
+ msg='edit for %s, %s: %s got where %s is attended' % (
+ username, urn, response.status_code, status_code))
diff --git a/chimere/views.py b/chimere/views.py
index f8581a7..e7a3059 100644
--- a/chimere/views.py
+++ b/chimere/views.py
@@ -230,7 +230,7 @@ def get_edit_page(redirect_url, item_cls, item_form,
Edition page
"""
def func(request, map_name="", item_id=None, cat_type=['M']):
- response_dct, redir = get_base_response(request, map_name)
+ response_dct, redir = get_base_response(request, map_name, edit=True)
if redir:
return redir, None, None
if 'map_name' in response_dct:
--
cgit v1.2.3
From 9a9e37ef2be3f07ac6efed8cb7319114b9803756 Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Wed, 7 Aug 2013 17:01:00 +0200
Subject: Admin: improve user add in groups
---
chimere/admin.py | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/chimere/admin.py b/chimere/admin.py
index 9f65707..3119b5d 100644
--- a/chimere/admin.py
+++ b/chimere/admin.py
@@ -25,6 +25,8 @@ import datetime
from django import forms
from django.conf import settings
from django.contrib import admin, messages
+from django.contrib.auth.models import User, Group
+from django.contrib.auth.admin import GroupAdmin
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Q
from django.http import HttpResponse, HttpResponseRedirect
@@ -47,6 +49,19 @@ from chimere.models import Category, Icon, SubCategory, Marker, \
from chimere.utils import unicode_normalize, ShapefileManager, KMLManager,\
CSVManager
+admin.site.unregister(Group)
+
+class UserInline(admin.StackedInline):
+ model = User.groups.through
+ verbose_name = _(u'User')
+ verbose_name_plural = _(u'User')
+ extra = 1
+
+class GroupAdmin(GroupAdmin):
+ inlines = [ UserInline, ]
+
+admin.site.register(Group, GroupAdmin)
+
def disable(modeladmin, request, queryset):
for item in queryset:
item.status = 'D'
--
cgit v1.2.3
From a6fc315c72946489a0c683a640f5953c5fb7e6b9 Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Wed, 7 Aug 2013 17:36:41 +0200
Subject: Better management of map rights
---
chimere/actions.py | 2 +-
chimere/models.py | 48 ++++++++++++++++++++++++++++++++++++++----------
chimere/views.py | 19 +++++--------------
3 files changed, 44 insertions(+), 25 deletions(-)
diff --git a/chimere/actions.py b/chimere/actions.py
index d9ed274..824b14f 100644
--- a/chimere/actions.py
+++ b/chimere/actions.py
@@ -42,7 +42,7 @@ DEFAULT_ACTIONS = [[Action('view', 'chimere:index', _('View')), []],
[Action('contribute', 'chimere:edit', _('Contribute'),
condition=lambda user, map_name:bool(
Map.getAvailable(user=user, urn=map_name, single=True,
- edit=True))),
+ propose=True))),
(Action('edit', 'chimere:edit', _('Add a new point of interest')),
Action('edit-route', 'chimere:editroute', _('Add a new route'))),
],
diff --git a/chimere/models.py b/chimere/models.py
index cd2996d..d5b3f1b 100644
--- a/chimere/models.py
+++ b/chimere/models.py
@@ -1397,8 +1397,17 @@ class Map(models.Model, SimpleArea):
ordering = ('order', 'name')
verbose_name = _("Map")
+ def can_write(self, user=None):
+ return bool(self.getAvailable(user=user, urn=self.urn, single=True,
+ edit=True))
+
+ def can_propose(self, user=None):
+ return bool(self.getAvailable(user=user, urn=self.urn, single=True,
+ propose=True))
+
@classmethod
- def getAvailable(cls, user=None, urn=None, single=False, edit=False):
+ def getAvailable(cls, user=None, urn=None, single=False, edit=False,
+ propose=False):
'''Get available maps
'''
map_filter = {'available':True}
@@ -1406,19 +1415,38 @@ class Map(models.Model, SimpleArea):
map_filter['urn'] = urn
elif single:
map_filter['default'] = True
- filters = [{'public_write':True},
- {'public_propose':True},
- ] if edit else [
- {'public_read':True}]
+ filters = []
+ if not propose and not edit:
+ filters = [{'public_write':True},
+ {'public_propose':True},
+ {'public_read':True}]
+ elif propose:
+ filters = [{'public_write':True},
+ {'public_propose':True}]
+ elif edit:
+ filters = [{'public_write':True}]
if user and user.is_authenticated():
- filters += [
+ if not propose and not edit:
+ filters += [
+ {'mapusers__user':user, 'mapusers__read':True},
{'mapusers__user':user, 'mapusers__write':True},
{'mapusers__user':user, 'mapusers__propose':True},
+ {'mapgroups__group__user':user, 'mapgroups__read':True},
{'mapgroups__group__user':user, 'mapgroups__write':True},
- {'mapgroups__group__user':user, 'mapgroups__propose':True},
- ] if edit else [
- {'mapusers__user':user, 'mapusers__read':True},
- {'mapgroups__group__user':user, 'mapgroups__read':True},]
+ {'mapgroups__group__user':user, 'mapgroups__propose':True}
+ ]
+ elif propose:
+ filters += [
+ {'mapusers__user':user, 'mapusers__write':True},
+ {'mapusers__user':user, 'mapusers__propose':True},
+ {'mapgroups__group__user':user, 'mapgroups__write':True},
+ {'mapgroups__group__user':user, 'mapgroups__propose':True}
+ ]
+ elif edit:
+ filters += [
+ {'mapusers__user':user, 'mapusers__write':True},
+ {'mapgroups__group__user':user, 'mapgroups__write':True},
+ ]
query = None
for fltr in filters:
fltr.update(map_filter)
diff --git a/chimere/views.py b/chimere/views.py
index e7a3059..6cd87d0 100644
--- a/chimere/views.py
+++ b/chimere/views.py
@@ -71,7 +71,7 @@ def get_base_uri(request):
return base_uri
#TODO: convert to requestcontext
-def get_base_response(request, map_name="", edit=False):
+def get_base_response(request, map_name="", propose=False):
"""
Get the base url
"""
@@ -88,24 +88,15 @@ def get_base_response(request, map_name="", edit=False):
base_url += '/'
base_url += map_name + '/'
base_response_dct['extra_url'] = base_url
- map = Map.getAvailable(user=request.user, urn=map_name, edit=edit,
+ map = Map.getAvailable(user=request.user, urn=map_name, propose=propose,
single=True)
if map:
map_name = map.urn
elif map_name:
return None, redirect(reverse('chimere:index'))
if map:
- base_response_dct['can_write'] = map.public_write
- base_response_dct['can_propose'] = map.public_propose
- if base_response_dct['is_authenticated']:
- base_response_dct['can_write'] = \
- base_response_dct['can_write'] or bool(
- Map.objects.filter(pk=map.pk, mapusers__user=request.user,
- mapusers__write=True).count())
- base_response_dct['can_propose'] = \
- base_response_dct['can_propose'] or bool(
- Map.objects.filter(pk=map.pk, mapusers__user=request.user,
- mapusers__propose=True).count())
+ base_response_dct['can_write'] = map.can_write(user=request.user)
+ base_response_dct['can_propose'] = map.can_propose(user=request.user)
base_response_dct['map'] = map
base_response_dct['map_name'] = map_name
@@ -230,7 +221,7 @@ def get_edit_page(redirect_url, item_cls, item_form,
Edition page
"""
def func(request, map_name="", item_id=None, cat_type=['M']):
- response_dct, redir = get_base_response(request, map_name, edit=True)
+ response_dct, redir = get_base_response(request, map_name, propose=True)
if redir:
return redir, None, None
if 'map_name' in response_dct:
--
cgit v1.2.3
From 918fe484138778d57f7ccef11063bec33d923339 Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Wed, 7 Aug 2013 18:10:21 +0200
Subject: Model: default fields NullBooleanField -> BooleanField
---
...eld_map_default__chg_field_maplayers_default.py | 345 +++++++++++++++++++++
chimere/models.py | 4 +-
2 files changed, 347 insertions(+), 2 deletions(-)
create mode 100644 chimere/migrations/0009_auto__chg_field_map_default__chg_field_maplayers_default.py
diff --git a/chimere/migrations/0009_auto__chg_field_map_default__chg_field_maplayers_default.py b/chimere/migrations/0009_auto__chg_field_map_default__chg_field_maplayers_default.py
new file mode 100644
index 0000000..5e65cda
--- /dev/null
+++ b/chimere/migrations/0009_auto__chg_field_map_default__chg_field_maplayers_default.py
@@ -0,0 +1,345 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Changing field 'Map.default'
+ db.execute('update chimere_map set "default"=False where "default" is NULL')
+ db.alter_column('chimere_map', 'default', self.gf('django.db.models.fields.BooleanField')())
+
+ # Changing field 'MapLayers.default'
+ db.execute('update chimere_maplayers set "default"=False where "default" is NULL')
+ db.alter_column('chimere_maplayers', 'default', self.gf('django.db.models.fields.BooleanField')())
+
+ def backwards(self, orm):
+
+ # Changing field 'Map.default'
+ db.alter_column('chimere_map', 'default', self.gf('django.db.models.fields.NullBooleanField')(null=True))
+
+ # Changing field 'MapLayers.default'
+ db.alter_column('chimere_maplayers', 'default', self.gf('django.db.models.fields.NullBooleanField')(null=True))
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'chimere.aggregatedroute': {
+ 'Meta': {'object_name': 'AggregatedRoute', 'db_table': "'chimere_aggregated_routes'", 'managed': 'False'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'route': ('django.contrib.gis.db.models.fields.MultiLineStringField', [], {}),
+ 'status': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'subcategory': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.SubCategory']"})
+ },
+ 'chimere.category': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'Category'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.color': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'Color'},
+ 'code': ('django.db.models.fields.CharField', [], {'max_length': '6'}),
+ 'color_theme': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'colors'", 'to': "orm['chimere.ColorTheme']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.colortheme': {
+ 'Meta': {'object_name': 'ColorTheme'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.icon': {
+ 'Meta': {'object_name': 'Icon'},
+ 'height': ('django.db.models.fields.IntegerField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'width': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.importer': {
+ 'Meta': {'object_name': 'Importer'},
+ 'associate_marker_to_way': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'categories': ('chimere.widgets.SelectMultipleField', [], {'to': "orm['chimere.SubCategory']", 'symmetrical': 'False'}),
+ 'default_description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'default_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'filtr': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'get_description': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'importer_type': ('django.db.models.fields.CharField', [], {'max_length': '4'}),
+ 'license': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'origin': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'overwrite': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'source': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'source_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'srid': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'state': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'zipped': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'chimere.layer': {
+ 'Meta': {'object_name': 'Layer'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layer_code': ('django.db.models.fields.TextField', [], {'max_length': '300'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.map': {
+ 'Meta': {'ordering': "('order', 'name')", 'object_name': 'Map'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'cluster': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'default': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'default_subcategories': ('chimere.widgets.SelectMultipleField', [], {'to': "orm['chimere.SubCategory']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'dynamic_categories': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'external_css': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layers': ('chimere.widgets.SelectMultipleField', [], {'symmetrical': 'False', 'related_name': "'maps'", 'blank': 'True', 'through': "orm['chimere.MapLayers']", 'to': "orm['chimere.Layer']"}),
+ 'lower_right_corner': ('django.contrib.gis.db.models.fields.PointField', [], {'default': "'POINT(0 0)'"}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'unique': 'True'}),
+ 'public_propose': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'public_read': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'public_write': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'restrict_to_extent': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'subcategories': ('chimere.widgets.SelectMultipleField', [], {'symmetrical': 'False', 'related_name': "'maps'", 'blank': 'True', 'db_table': "'chimere_subcategory_maps'", 'to': "orm['chimere.SubCategory']"}),
+ 'upper_left_corner': ('django.contrib.gis.db.models.fields.PointField', [], {'default': "'POINT(0 0)'"}),
+ 'urn': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'blank': 'True'}),
+ 'users': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'through': "orm['chimere.MapUsers']", 'symmetrical': 'False'}),
+ 'welcome_message': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.mapgroups': {
+ 'Meta': {'object_name': 'MapGroups'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'mapgroups'", 'to': "orm['auth.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'map': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'mapgroups'", 'to': "orm['chimere.Map']"}),
+ 'propose': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'read': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'write': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'chimere.maplayers': {
+ 'Meta': {'ordering': "('order',)", 'object_name': 'MapLayers'},
+ 'default': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.Layer']"}),
+ 'map': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.Map']"}),
+ 'order': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.mapusers': {
+ 'Meta': {'object_name': 'MapUsers'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'map': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'mapusers'", 'to': "orm['chimere.Map']"}),
+ 'propose': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'read': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'mapusers'", 'to': "orm['auth.User']"}),
+ 'write': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'chimere.marker': {
+ 'Meta': {'ordering': "('status', 'name')", 'object_name': 'Marker'},
+ 'available_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'categories': ('chimere.widgets.SelectMultipleField', [], {'to': "orm['chimere.SubCategory']", 'symmetrical': 'False'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'end_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'import_key': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'import_source': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'import_version': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'is_front_page': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'license': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'modified_since_import': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'not_for_osm': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'origin': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'point': ('chimere.widgets.PointField', [], {}),
+ 'ref_item': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'submited_marker'", 'null': 'True', 'to': "orm['chimere.Marker']"}),
+ 'route': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'associated_marker'", 'null': 'True', 'to': "orm['chimere.Route']"}),
+ 'start_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'status': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'submiter_comment': ('django.db.models.fields.TextField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'submiter_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
+ 'submiter_name': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
+ 'submiter_session_key': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
+ 'weight': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.multimediaextension': {
+ 'Meta': {'object_name': 'MultimediaExtension'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'multimedia_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'extensions'", 'to': "orm['chimere.MultimediaType']"}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '6'})
+ },
+ 'chimere.multimediafile': {
+ 'Meta': {'object_name': 'MultimediaFile'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'marker': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'multimedia_files'", 'to': "orm['chimere.Marker']"}),
+ 'miniature': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'multimedia_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.MultimediaType']", 'null': 'True', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'url': ('django.db.models.fields.URLField', [], {'max_length': '200'})
+ },
+ 'chimere.multimediatype': {
+ 'Meta': {'object_name': 'MultimediaType'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'iframe': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'media_type': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'mime_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.news': {
+ 'Meta': {'object_name': 'News'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'content': ('django.db.models.fields.TextField', [], {}),
+ 'date': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_front_page': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'maps': ('chimere.widgets.SelectMultipleField', [], {'symmetrical': 'False', 'to': "orm['chimere.Map']", 'null': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.page': {
+ 'Meta': {'object_name': 'Page'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'content': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mnemonic': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '10', 'null': 'True', 'blank': 'True'}),
+ 'template_path': ('django.db.models.fields.CharField', [], {'max_length': '150', 'null': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.picturefile': {
+ 'Meta': {'object_name': 'PictureFile'},
+ 'height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'marker': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'pictures'", 'to': "orm['chimere.Marker']"}),
+ 'miniature': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'picture': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
+ 'thumbnailfile': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'thumbnailfile_height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'thumbnailfile_width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.property': {
+ 'Meta': {'object_name': 'Property'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'marker': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.Marker']"}),
+ 'propertymodel': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.PropertyModel']"}),
+ 'value': ('django.db.models.fields.TextField', [], {})
+ },
+ 'chimere.propertymodel': {
+ 'Meta': {'ordering': "('order',)", 'object_name': 'PropertyModel'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mandatory': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {}),
+ 'subcategories': ('chimere.widgets.SelectMultipleField', [], {'symmetrical': 'False', 'related_name': "'properties'", 'blank': 'True', 'to': "orm['chimere.SubCategory']"}),
+ 'type': ('django.db.models.fields.CharField', [], {'max_length': '1'})
+ },
+ 'chimere.propertymodelchoice': {
+ 'Meta': {'object_name': 'PropertyModelChoice'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'propertymodel': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'choices'", 'to': "orm['chimere.PropertyModel']"}),
+ 'value': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.route': {
+ 'Meta': {'ordering': "('status', 'name')", 'object_name': 'Route'},
+ 'associated_file': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.RouteFile']", 'null': 'True', 'blank': 'True'}),
+ 'categories': ('chimere.widgets.SelectMultipleField', [], {'to': "orm['chimere.SubCategory']", 'symmetrical': 'False'}),
+ 'end_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'has_associated_marker': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'import_key': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'import_source': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'import_version': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'license': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'modified_since_import': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'not_for_osm': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'origin': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'picture': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'ref_item': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'submited_route'", 'null': 'True', 'to': "orm['chimere.Route']"}),
+ 'route': ('chimere.widgets.RouteField', [], {}),
+ 'start_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'status': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'submiter_comment': ('django.db.models.fields.TextField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'submiter_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
+ 'submiter_name': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
+ 'submiter_session_key': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
+ 'width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.routefile': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'RouteFile'},
+ 'file_type': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'raw_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+ 'simplified_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.subcategory': {
+ 'Meta': {'ordering': "['category', 'order']", 'object_name': 'SubCategory'},
+ 'as_layer': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'category': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'subcategories'", 'to': "orm['chimere.Category']"}),
+ 'color_theme': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.ColorTheme']", 'null': 'True', 'blank': 'True'}),
+ 'dated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'hover_icon': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'subcat_hovered'", 'null': 'True', 'to': "orm['chimere.Icon']"}),
+ 'icon': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.Icon']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'item_type': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '1000'}),
+ 'routing_warn': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'submission': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'weighted': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'chimere.tinyurl': {
+ 'Meta': {'object_name': 'TinyUrl'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'parameters': ('django.db.models.fields.CharField', [], {'max_length': '500'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['chimere']
diff --git a/chimere/models.py b/chimere/models.py
index d5b3f1b..764a4b0 100644
--- a/chimere/models.py
+++ b/chimere/models.py
@@ -1364,7 +1364,7 @@ class Map(models.Model, SimpleArea):
default='POINT(0 0)', srid=settings.CHIMERE_EPSG_DISPLAY_PROJECTION)
lower_right_corner = models.PointField(_(u"Lower right corner"),
default='POINT(0 0)', srid=settings.CHIMERE_EPSG_DISPLAY_PROJECTION)
- default = models.NullBooleanField(_(u"Default map"),
+ default = models.BooleanField(_(u"Default map"), default=False,
help_text=_(u"Only one map is set by default"))
layers = SelectMultipleField(Layer, related_name='maps',
through='MapLayers', blank=True)
@@ -1611,7 +1611,7 @@ class MapLayers(models.Model):
map = models.ForeignKey(Map)
layer = models.ForeignKey(Layer)
order = models.IntegerField(_(u"Order"))
- default = models.NullBooleanField(_(u"Default layer"))
+ default = models.BooleanField(_(u"Default layer"), default=False)
class Meta:
ordering = ('order',)
--
cgit v1.2.3
From a541275847b3f9b423186269d94b5a33b3ddd9c1 Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Wed, 7 Aug 2013 18:11:15 +0200
Subject: Fixtures: move Openlayers layers to a specific file
---
chimere/fixtures/initial_data.json | 32 -----------------------
chimere/fixtures/layers-default-openlayers.json | 34 +++++++++++++++++++++++++
2 files changed, 34 insertions(+), 32 deletions(-)
create mode 100644 chimere/fixtures/layers-default-openlayers.json
diff --git a/chimere/fixtures/initial_data.json b/chimere/fixtures/initial_data.json
index c44eb63..cff5710 100644
--- a/chimere/fixtures/initial_data.json
+++ b/chimere/fixtures/initial_data.json
@@ -189,37 +189,5 @@
"multimedia_type": 13,
"name": "webm"
}
- },
- {
- "pk": 1,
- "model": "chimere.layer",
- "fields": {
- "layer_code": "new OpenLayers.Layer.OSM.Mapnik(\"Mapnik OSM\",\r\n{attribution:\"\", keyid:\"mapnik\", displayOutsideMaxExtent:!0,wrapDateLine:!0,layerCode:\"M\"})",
- "name": "OSM - Mapnik"
- }
- },
- {
- "pk": 2,
- "model": "chimere.layer",
- "fields": {
- "layer_code": "new OpenLayers.Layer.OSM.CycleMap(\"Cycle map\",\r\n{attribution:\"Tiles courtesy of Andy Allan \",\r\nkeyid:\"cyclemap\",displayOutsideMaxExtent:!0,wrapDateLine:!0,layerCode:\"C\"})",
- "name": "OSM - CycleMap"
- }
- },
- {
- "pk": 3,
- "model": "chimere.layer",
- "fields": {
- "layer_code": "new OpenLayers.Layer.OSM.TransportMap(\"Transport map\",\r\n{attribution:\"Tiles courtesy of Andy Allan \",\r\nkeyid:\"transportmap\",displayOutsideMaxExtent:!0,wrapDateLine:!0,layerCode:\"T\"})",
- "name": "OSM - TransportMap"
- }
- },
- {
- "pk": 4,
- "model": "chimere.layer",
- "fields": {
- "layer_code": "new OpenLayers.Layer.MapQuestOSM()",
- "name": "OSM - MapQuest"
- }
}
]
diff --git a/chimere/fixtures/layers-default-openlayers.json b/chimere/fixtures/layers-default-openlayers.json
new file mode 100644
index 0000000..6ab3f3e
--- /dev/null
+++ b/chimere/fixtures/layers-default-openlayers.json
@@ -0,0 +1,34 @@
+[
+ {
+ "pk": 1,
+ "model": "chimere.layer",
+ "fields": {
+ "layer_code": "new OpenLayers.Layer.OSM.Mapnik(\"Mapnik OSM\",\r\n{attribution:\"\", keyid:\"mapnik\", displayOutsideMaxExtent:!0,wrapDateLine:!0,layerCode:\"M\"})",
+ "name": "OSM - Mapnik"
+ }
+ },
+ {
+ "pk": 2,
+ "model": "chimere.layer",
+ "fields": {
+ "layer_code": "new OpenLayers.Layer.OSM.CycleMap(\"Cycle map\",\r\n{attribution:\"Tiles courtesy of Andy Allan \",\r\nkeyid:\"cyclemap\",displayOutsideMaxExtent:!0,wrapDateLine:!0,layerCode:\"C\"})",
+ "name": "OSM - CycleMap"
+ }
+ },
+ {
+ "pk": 3,
+ "model": "chimere.layer",
+ "fields": {
+ "layer_code": "new OpenLayers.Layer.OSM.TransportMap(\"Transport map\",\r\n{attribution:\"Tiles courtesy of Andy Allan \",\r\nkeyid:\"transportmap\",displayOutsideMaxExtent:!0,wrapDateLine:!0,layerCode:\"T\"})",
+ "name": "OSM - TransportMap"
+ }
+ },
+ {
+ "pk": 4,
+ "model": "chimere.layer",
+ "fields": {
+ "layer_code": "new OpenLayers.Layer.MapQuestOSM()",
+ "name": "OSM - MapQuest"
+ }
+ }
+]
--
cgit v1.2.3
From 4cb7d312d9f17a722207624c0f0977b43b5416d2 Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Thu, 8 Aug 2013 18:47:23 +0200
Subject: Refactoring: nominatim widget -> template
---
.../templates/chimere/blocks/nominatim_widget.html | 13 +++++++++++++
chimere/widgets.py | 21 +++++----------------
2 files changed, 18 insertions(+), 16 deletions(-)
create mode 100644 chimere/templates/chimere/blocks/nominatim_widget.html
diff --git a/chimere/templates/chimere/blocks/nominatim_widget.html b/chimere/templates/chimere/blocks/nominatim_widget.html
new file mode 100644
index 0000000..442ffed
--- /dev/null
+++ b/chimere/templates/chimere/blocks/nominatim_widget.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
diff --git a/chimere/widgets.py b/chimere/widgets.py
index 8bed606..b3ec156 100644
--- a/chimere/widgets.py
+++ b/chimere/widgets.py
@@ -226,22 +226,11 @@ class NominatimWidget(forms.TextInput):
class Media:
js = ["%schimere/js/nominatim-widget.js" % settings.STATIC_URL]
def render(self, name, value, attrs=None, map_name=''):
- tpl = u"""
-
-
-
-
-
-""" % {'id':name, 'nominatim_url':settings.NOMINATIM_URL,
- 'label':_(u"Street, City, Country")}
- return mark_safe(tpl)
+ return mark_safe(
+ render_to_string('chimere/blocks/nominatim_widget.html',
+ {'id':name, 'nominatim_url':settings.NOMINATIM_URL,
+ 'label':_(u"Street, City, Country")}
+ ))
class PointChooserWidget(forms.TextInput):
"""
--
cgit v1.2.3
From bfc5f255441685871f3de56cdede9448288cc7cc Mon Sep 17 00:00:00 2001
From: Étienne Loks
Date: Thu, 8 Aug 2013 20:16:40 +0200
Subject: Configure Nominatim widget for edit page
---
chimere/static/chimere/css/styles.css | 5 +++++
chimere/static/chimere/js/jquery.chimere-ol.js | 12 ++++++++++++
chimere/static/chimere/js/nominatim-widget.js | 7 ++++++-
chimere/templates/chimere/blocks/live_coordinates.html | 4 +---
chimere/templates/chimere/blocks/nominatim_widget.html | 6 +++++-
chimere/templates/chimere/edit.html | 3 ++-
chimere/views.py | 5 +++--
chimere/widgets.py | 5 ++---
8 files changed, 36 insertions(+), 11 deletions(-)
diff --git a/chimere/static/chimere/css/styles.css b/chimere/static/chimere/css/styles.css
index 22138ae..0f17640 100644
--- a/chimere/static/chimere/css/styles.css
+++ b/chimere/static/chimere/css/styles.css
@@ -31,6 +31,7 @@ h2, h3, th, .action li, .action li a,
color:#aaa;
}
+.nominatim-search,
#chimere_total_label td.l{
color:#000;
}
@@ -611,6 +612,10 @@ ul.share li{
font-style:italic;
}
+.nominatim-search{
+ font-style:normal;
+}
+
.simple #panel{
top:5px;
bottom:auto;
diff --git a/chimere/static/chimere/js/jquery.chimere-ol.js b/chimere/static/chimere/js/jquery.chimere-ol.js
index ff07132..98fbb48 100644
--- a/chimere/static/chimere/js/jquery.chimere-ol.js
+++ b/chimere/static/chimere/js/jquery.chimere-ol.js
@@ -987,6 +987,18 @@ if (typeof(OpenLayers) != 'undefined'){
settings.map.getProjectionObject());
settings.layerVectors.addFeatures(feats);
},
+ pointInputChange: function(nominatim_id){
+ methods.putEditMarker(new OpenLayers.LonLat(
+ $('#'+nominatim_id+'_lon').val(),
+ $('#'+nominatim_id+'_lat').val()
+ ).transform(EPSG_DISPLAY_PROJECTION,
+ settings.map.getProjectionObject()), false);
+ var bounds = settings.layerMarkers.getDataExtent();
+ if (bounds) {
+ settings.map.zoomToExtent(bounds);
+ settings.map.zoomTo(13);
+ }
+ },
routingInputChange: function(nominatim_id){
$('#map_menu_clear').show();
switch(nominatim_id){
diff --git a/chimere/static/chimere/js/nominatim-widget.js b/chimere/static/chimere/js/nominatim-widget.js
index 99f7034..d61443f 100644
--- a/chimere/static/chimere/js/nominatim-widget.js
+++ b/chimere/static/chimere/js/nominatim-widget.js
@@ -1,4 +1,5 @@
var default_nominatim_lbl = '';
+var routing = false;
var nominatim_widget_options = {
source: function (request, response) {
$.ajax({
@@ -30,7 +31,11 @@ var nominatim_widget_options = {
$('#chimere_'+$(this).attr('id').substring(10)+'_label').html(
ui.item.label);
$('#'+$(this).attr('id')).val(default_nominatim_lbl);
- jQuery("#map").chimere("routingInputChange", $(this).attr('id'));
+ if (routing){
+ jQuery("#"+chimere_init_options['map_id']).chimere("routingInputChange", $(this).attr('id'));
+ } else {
+ jQuery("#"+chimere_init_options['map_id']).chimere("pointInputChange", $(this).attr('id'));
+ }
return false;
},
open: function() {
diff --git a/chimere/templates/chimere/blocks/live_coordinates.html b/chimere/templates/chimere/blocks/live_coordinates.html
index 4392dca..3ce34bf 100644
--- a/chimere/templates/chimere/blocks/live_coordinates.html
+++ b/chimere/templates/chimere/blocks/live_coordinates.html
@@ -15,9 +15,6 @@
if({{default_map}}) chimere_init_options['selected_map_layer'] = {{default_map}};
-
-
-