Leaflet upgrade (#7855)

* Squashed 'lib/Leaflet.markercluster/' changes from e96388d6c..584add242

584add242 Merge pull request #846 from ghybs/spiderfyLegFix
e8ca4e8c6 Refactor(Spiderfier): circle spiderfy clockwise like spiral
949d88ae6 Fix(Spiderfier): min leg distance for circle and skip 1st pos for spiral
645e3a8b3 Bump this off
ed8e05ab5 1.2.0
e8219ecd3 Merge pull request #838 from lucaswerkmeister/836
17e8cb710 Support DistanceGrid with cell size 0
cc61460fc Make DistanceGrid cells closed sets
00a24788b Merge pull request #832 from ghybs/refactorOptions
a256831f2 Refactor(examplePane): assign clusterPane option only if pane is provided
61e06292d Refactor(options): move clusterPane into options field
0a00ec3a6 Merge pull request #831 from adammertel/master
644663697 new subplugin in readme - MarkerCluster.List
f175a7cc3 1.1.0
d077f4920 Merge pull request #819 from ckrahe/panes
37bc74004 Default group clusterPane to L.Marker default
f80ee8bce Rename option for clarity
bd3748a89 Add testing section.
90004e431 Change option name to avoid collision with Leaflet. Document default. Add pane unit tests.
e8f27e469 Follow standard and fully-qualify example references.
480c78698 Create pane example. Add second link to custom example.
4fffa0aa5 Support optional pane
f1b0d8fe3 Other 1.0.6
80570d1ec Update changelog for 1.0.6
397693fa8 Be a bit more careful with minZoom. refs #789
eab34569c Merge pull request #794 from ghybs/readme-https
e2d5201d8 Merge pull request #795 from ghybs/examples-https
b5afe4fe8 Refactor(examples/debug): use HTTPS for CDN, Tiles & OSM link
77fbc29d5 Docs(README): use https for demo links
e387e1f48 Fix non-integer min/max zoom and add a test. Ref #789
4062f3768 Remove mention of custom build stuff that hasn't existed for a while
c56a60d51 Fix addressPoints2 usage in example. Fixes #781
e9cc1e29f Bump for 1.0.5
590cce513 Merge pull request #779 from timkelty/pass-fitBounds-options
4a2399606 README for fitBounds options Pass undefined
97c3bb093 Allow paddling fitBounds options to zoomToBounds
d7288c7fe Merge pull request #776 from kent1D/patch-1
fcf9e9dd5 Update CHANGELOG.md
254187999 Remove unnecessary undefined check, this is set to null by default.
f01f8b1fe Merge pull request #773 from MrCheeze/master
ea9400017 Fixed check to match coding standards (!==)
b97aa03af Fixed bug where disableClusteringAtZoom being 0 is treated the same as null
80dad1839 Bump for 1.0.4
ac052696a add subplugin Leaflet.MarkerCluster.PlacementStrategies (#766)
162f15722 Pull minZoom fetching out to fix errors removing a MCG from a map while an animation is going on.
31e413d28 Add a note about the JS files
701591250 bump readme to 1.0.3 (#756)
588765b71 Bump for 1.0.3
c82f34252 Another test and fix for #753
706d6f266 Test and fix for moving a marker while the MCG is off the map. Refs #753
848a172a5 Bump version
a44c7c402 Update CHANGELOG for 1.0.2
cf26d0190 Update changelog for 1.0.0
b67410966 If a marker is moved with an open popup on it, re-open the popup after moving it. Fixes #651
16cf328a5 Support layerremove event. fixes #647
2a88279e9 Support layeradd event. refs #647
dbdedb93e Tidy up all these event handlers we now need to add.
cbfdb9806 Fix a few more cases where negative minZoom wasn't handled. refs #704
e65cb900c Fix an issue when opening a popup inside of zoomToShowLayer. If there was an existing spiderfied marker with a popup on it, the map would scroll back to the location of that marker.
37d5143a7 Merge pull request #747 from z3ut/fix-zoom-to-show-layer
63c648177 Add support for maps with negative minZoom. Fixes #704
0f8b3d102 Bump version
ee69a43f8 Add an example with draggable markers. Fix dragging markers. Fixes #685
f6379783a Bump all examples to leaflet 1.0.3
1e81a9aba Add an ISSUE_TEMPLATE based on the leaflet one
8bb6278e7 Remove zoomToShowLayer moveStart event check
9c5fe783b Add zoomToShowLayer tests
3468278a7 Fixed zoomToShowLayer() markers disappearing bug
7db30166f Merge pull request #746 from marcianoviereck92/patch-1
fa20d91b0 Fixed leaflet 1.0.2 bug where clearLayers would throw an exception
ad023e9ef Merge pull request #738 from bertyhell/patch-1
7872b96e8 Update example map link
a1d98d65f Add example image of the marker cluster map
7b577c1b6 Add example image to readme
87cd5fd2f Add example image
e6a24dc50 Merge pull request #734 from ghybs/patch-3
76f0b0f30 README corrected iconCreateFunction
771b41b3e README improve plugin install instructions
d4d93457f README correct TOC order
29972bef5 Add install and build steps with jake (#733)
bd8f1369a Revert "npm instructions for 1.0.0 in readme"
c0ac83899 npm instructions for 1.0.0 in readme
f22d877c1 Should bump leaflet version here too
dd55b4bcc Update bower.json for 1.0.0 too
9ac8ee068 Bump package.json 1.0.0
a806d5064 Bump readme for 1.0.0
7da6fb2ad Merge pull request #656 from ghybs/recursively-simplified
bbd5dad61 Merge pull request #703 from tyleralves/patch-2
2c4123994 Merge pull request #708 from ghybs/addLayerFreezableCompatibility
4fce18cfe Added missing rounding to map._zoom
f87592144 Get zoom from internal _zoom instead of map.getZoom()
0e5c3347f Remove leaflet from peerDependencies (Leaflet/Leaflet#4534)
7c894bf72 Merge pull request #697 from qedsoftware/bulk-icon-performance
53c1f94a1 Refresh icons after bulk action, not during one
7b360f931 Merge pull request #700 from qedsoftware/travis
49a4dc445 Fix travis build
7269ac32a Merge pull request #690 from LukeSwart/patch-1
d4a2d05b9 Update README.md
05e10293d Merge pull request #672 from ghybs/patch-3
cab42e4ee Replaced links to gh-pages by github.io
5171f18bb Update npm install location
c0051f0a9 Bump version number to match leaflet
3ab1c69da Merge pull request #665 from hyperknot/rc1
c578c76ed updated peer dep to leaflet rc1
68ca95f79 Update README.md
5bb70eae9 Merge pull request #657 from ghybs/spiderfyCombinedWithDisableClustering
617b8f3f7 Merge pull request #658 from ghybs/patch-2
cc7aea0cc Replaced `dependencies` by `peerDependencies`
6ee9972ab Copied note for `spiderfyOnMaxZoom` option explanation.
e4dca376f Merged conditions for zoomOrSpiderfy to avoid no action with disableClusteringAtZoom
c78e73987 Created disableClusteringAtZoomSpec
8bdb884c8 Simplified _recursively
d91addd7b Add building instructions
cb05eb292 Merge pull request #644 from ghybs/master
44963c185 Corrected #529 in master (Leaflet 1.0.0)
a4c3bce8b Remove built files, we only ship these in tags now. BYO otherwise.
2d6b625d9 Merge pull request #617 from zverev/unspiderfy
f2ea5b5db Merge pull request #624 from ghybs/addNestedLayerGroups
33e5eec4f Clone input array at first group met
088001092 Merge pull request #626 from isedwards/geojson_semicolons
9d7731600 Added a couple of missing semi-colons to improve readability/best practice.
9b451926f Manage nested groups in removeLayer and removeLayers as well
01467aa41 Missing final blank line in AddLayersSpec
7cf8152dd Manage adding nested Layer Groups
bfc877c8a Use the new travis infrastructure hopefully
2faaba2cb Add unspiderfy test
37fce91f2 Merge branch 'master' into unspiderfy
54976bf69 fix a typo
f61cfafc0 Public unspiderfy method
ba3c95b27 Update CHANGELOG
5f88d2428 Update leaflet version in package.json and format
43b094824 Grab leaflet from https always. Fixes #614
57cf04cdc Don't return markers that need to be removed in eachLayer/getLayers.
a44af7231 Update readme for 1.0.0
1b68284d8 Add test from calvinmetcalf in #397
e18deb3a2 Add missing tests to spec/index.html runner
9f6125501 Fix spiderfySpec tests
97e27e173 Merge branch 'master' into leaflet-master-merge
8ecc49410 Merge pull request #603 from ghybs/leaflet-master-merge
822131832 Documentation for unspiderfied event
de4d3321c Test for unspiderfied event
e3017c5e4 Merge remote-tracking branch 'newmanw/master'
6a93ee3a1 Add and fix tests from #421
e5ab67d26 Merge commit 'ac8c7063af5ea8e9cce4ce595c7d8303dc595c17'
d9c131036 Merge pull request #609 from OriginalSin/OriginalSin-patch-1
58480310d Merge pull request #607 from ghybs/removeAnimClassOnMapRemovedButSpiderfied
ad78e3c02 Merge pull request #610 from ghybs/patch-1
c1b79349e Added sub-plugin MCG.Freezable
ed9f02e6a Update MarkerClusterGroup.js
fd59fa494 Added missing blank line end of file spiderfySpec
e894b31fc Added listener de-binding on remove
4360bf546 Added test in spiderfySpec for class add/remove
c4bc5759a Corrected _spiderfierOnRemove to use no animation
a0ccbe062 Merge pull request #604 from ghybs/correctUnspiderfyVector
9a212d189 Merge pull request #606 from ghybs/spiderfyOnAllChildrenInBottomMostCluster
f18b7f9da Improved methods existence checks
4bb81fda0 Correct smart tab issue
cf2f310d2 Added spiderfy test for current zoom
8bb5e64a6 Simplified spiderfy at current zoom trigger
8e2e653ac Improved _zoomOrSpiderfy
6084aa7bb Removed useless comment
c338c7777 Removed useless comment
a4ec12ceb Replaced instanceof L.Marker by .setOpacity check
088b6ae7d Replaced instanceof L.Marker by .setOpacity check
f1ec8c5e0 Correct unspiderfy L.Circle and removeOutsideVisibleBoundsSpec
6328db92f Added magic-string devDependency
d37d0ed80 Updated examples script src to v1.0.0-beta.2 (leaflet-src.js)
b0e2acf27 Merged new commits and corrected all test suites
c69219fa4 Merge pull request #602 from tomchadwin/master
79abfe1bd Formatting typo
1a7d012cd Merge remote-tracking branch 'upstream/leaflet-master-merge' into leaflet-master-merge
5fcd073a8 Merge pull request #600 from ghybs/readmeToc
1226ecb43 Corrected sub-levels indentation
6c7da0549 Added TOC into README
b348916ca Merge pull request #599 from ghybs/master
d30f3aec8 README new sub-plugins section added links
e80c1e0cd README added sub-plugins section
050145c29 Merge pull request #587 from ghybs/maxLatitude
eb71a8f84 Merge pull request #588 from ghybs/patch-1
3e21068b3 Merge pull request #585 from ghybs/animationSpiderfyNoSMIL
ab451fc3f Added reference to chunked addLayers explanation
183cb4686 Removed no longer needed code
d76e9a467 Improved naming of _checkBoundsMaxLat
aa1ba5207 Corrected test suite for max latitude check
95dbec9ec Added bounds expansion to Infinity
cd4f76dec Merge pull request #584 from ghybs/addLayers-Optimization
71ce1a184 Corrected legOptions class assignment
d6d4e7f89 Corrected spiderfy leg anim and leg opacity option
984c632ab Replaced spiderfy legs animation from SMIL to CSS transition
bf228d5f7 Removed class extension
1c4a5f62c Replaced bounds reset method extension by cluster private method
d55e21919 Merge pull request #583 from ghybs/refreshClusters
8a9f126b5 Added test for refresh singleMarkerMode reject alien markers
54c642c13 Added hasLayer check for singleMarkerMode refresh
127d367c2 Converted singleMarkerMode and animateOption specs
d9441c78c Reset bounds rather than re-create
7bbf70e47 Externalized bound and wLatLng computations
0df95323b Merge pull request #579 from ghybs/patch-1
e09833063 Refactored RefreshSpec to use before/afterEach
845f70236 Separated code for singleMarkerMode refresh
e6f36e0da Made refreshClusters to re-draw markers in singleMarkerMode
37e923719 Added getChildCount and getAllChildMarkers
3d08cb1a8 Typos
f56c4fa56 Typo and re-organized methods
7fd26abea Small punctuation improvements
4b3931f38 Corrected chunk* options description
0aaa590e4 Merge remote-tracking branch 'upstream/master' into patch-1
88233ae36 Merge branch 'patch-1' of https://github.com/ghybs/Leaflet.markercluster into patch-1
c24555196 Parse id to an int, fixes #531
29d7c6e0a Merge pull request #565 from Cyrille37/patch-1
e97134b5e Merge pull request #578 from ghybs/animateOption
739c6f10a Added spiderLegPolylineOptions in section All options
d6966aa17 Added description of chunk* options for addLayers
52d238af5 Added animate option
4aeb0dff6 Merge pull request #575 from ghybs/removeOutsideVisibleBounds
134d0a8bd Merge pull request #574 from ghybs/patch-1
60746cc16 Merge pull request #577 from ghybs/refreshClusters
69e142d65 Converted removeOutsideVisibleBoundsSpec test suite
e5fabbe0e Improved reliability of RefreshSpec test suite
957151362 Clean remote
e7efb8d76 Merge branch 'removeOutsideVisibleBounds' of https://github.com/ghybs/Leaflet.markercluster into removeOutsideVisibleBounds
f8a1ff926 Corrected effect of removeOutsideVisibleBounds option
feb518cc3 Corrected example for refreshIconOptions
cc8de424b Improved example for refreshIconOptions
603b85788 Corrected typo
43dda33b6 Don't need to keep track of the issues this was added for
63ed8842e Merge pull request #564 from ghybs/master
5b4dc71c1 Merge pull request #572 from ghybs/patch-2
50bb1a383 Merge pull request #571 from ghybs/patch-1
60360b13b Improved zoomToShowLayer with callback check
d5c7c9460 Replaced constructors by factories
61d24ffdd typo corrected
67fec60ce Added Refresh src in spec/index, newline at end of spec/suites/refresh
dea22170d Updated README
be3cacca5 Added license in package.json, trying to revert dist files
d1b11d9e6 Sync NPM dependencies so test suites are run against Leaflet 1.0.0-beta1
ac71269a4 Merge branch 'leaflet-master' into leaflet-master-merge
306ef6b62 Re-built dist files to include Refresh methods
d56061f87 Disabled animation in test suite and added new src file in spec index
3fb556586 Modified method names, added new file to build script, build dist files, added license to package.josn, created test spec suite and added it to spec index.
fe8949348 in section "Events" added "clustermouseout" event
a12254c67 Corrected compatibility with MCG and added direct refresh of MCG when refreshing Marker
a06712053 Additional methods to facilitate marker icons update and propagate to parent clusters in real-time. For issues #561, #555, #535 and #498.
49c92f8f4 placed new file in src
b91a135e5 Merge branch 'master' of https://github.com/ghybs/Leaflet.markercluster-1
afc132d21 first commit
10d440ba4 Rough contributing file, stolen from leaflet version
64a2d5711 Merge pull request #556 from Intermaps/remove-layers-fix
879aa2897 corrected explanation text
3c3a9aad9 Moved files to sub-folder and added explanation text and page formatting
2fe03885f first commit of gh-pages for leaflet marker cluster test of hierarchical tree
da64e04a1 add spec for removing layers when the group is not on the map
a6a897670 remove duplicate _arraySplice
801f6443f fix removeLayers when cluster is not on the map
a415818d4 Merge pull request #553 from cyrilchaponeverysens/master
cfb91e1c5 Revert "Added "main" property to bower.json to make it compatible with wiredep" (there was already a valid main section)
44bd117de Merge pull request #533 from olive380/patch-2
84da57e41 Update QuickHullSpec.js
3fd3e0d65 Delete showCoverageOnHover-vertically-aligned-markers.html
9a29fd0b6 Delete showCoverageOnHover-horizontally-aligned-markers.html
e2be3564b Merge pull request #532 from olive380/patch-1
8d2ae0b73 Create remove-geoJSON-when-spiderfied.html
468241db1 Create showCoverageOnHover-horizontally-aligned-markers.html
368076a14 Create showCoverageOnHover-vertically-aligned-markers.html
e6aa0b253 Update MarkerClusterGroup.js
502d8ac2c Update MarkerClusterGroup.js
a2f509ce4 Update MarkerCluster.QuickHull.js
0fe481dab Update MarkerCluster.QuickHull.js
79e65560d Update MarkerCluster.QuickHull.js
e9889719d Update MarkerCluster.QuickHull.js
59a934408 Update MarkerClusterGroup.js
102f14718 Merge pull request #472 from Eschon/leaflet-master
6e35cf74b rounded zoom to work with fractional zoom levels
53997cd3e Fire unspiderfied event
ac8c7063a Fix indention from spaces to tabs
90fbaca19 Provide more detailed context information on the spiderfied event
a1e06288d Fix for the latest leaflet master changes. fixes #289
517ba04fc We weren't using the chunked option, fixed.
920c58f09 Small bug still using old variable
530ccaf1a Specifically ignore out setLatLng calls. Should fix using this with Leaflet.Label (which out don't setLatLng hack broke)
f86404e84 Revert "Cludge around setLatLng so we don't cause 'move' events when we hack-move markers. This wants doing better."
db142ea67 Working animated spiderfying I think...
4e3b8252f Some of animated stuff works. The lines don't animate as we aren't adding the vector layer initially any more and some other bits are broken too
542892d6e get non-animated spiderfying working.
27827b5d6 Rough marker move tracking. refs #57
51014578d Cludge around setLatLng so we don't cause 'move' events when we hack-move markers. This wants doing better.
bc463e42c Change event propagation overrides to work with leaflet master code.

git-subtree-dir: lib/Leaflet.markercluster
git-subtree-split: 584add242e2a190bc30bf86e9d2bae44bd362722

* Squashed 'lib/Leaflet.awesome-markers/' changes from 4dfeef694..734042aef

734042aef Merge pull request #90 from fraulyoner/2.0/develop
23a8efa22 Fix typos in README

git-subtree-dir: lib/Leaflet.awesome-markers
git-subtree-split: 734042aef260eb3d9b72bbac5db00dfc07dbc602

* webui: Updated leaflet version
This commit is contained in:
Neil Lathwood
2017-12-05 08:13:13 +00:00
committed by GitHub
parent fed75d0aeb
commit 8b97d01be6
81 changed files with 8093 additions and 3152 deletions
+197 -43
View File
@@ -1,16 +1,12 @@
/* required styles */
.leaflet-map-pane,
.leaflet-pane,
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow,
.leaflet-tile-pane,
.leaflet-tile-container,
.leaflet-overlay-pane,
.leaflet-shadow-pane,
.leaflet-marker-pane,
.leaflet-popup-pane,
.leaflet-overlay-pane svg,
.leaflet-pane > svg,
.leaflet-pane > canvas,
.leaflet-zoom-box,
.leaflet-image-layer,
.leaflet-layer {
@@ -20,7 +16,6 @@
}
.leaflet-container {
overflow: hidden;
-ms-touch-action: none;
}
.leaflet-tile,
.leaflet-marker-icon,
@@ -28,20 +23,49 @@
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
-webkit-user-drag: none;
-webkit-user-drag: none;
}
/* Safari renders non-retina tile on retina better with this, but Chrome is worse */
.leaflet-safari .leaflet-tile {
image-rendering: -webkit-optimize-contrast;
}
/* hack that prevents hw layers "stretching" when loading new tiles */
.leaflet-safari .leaflet-tile-container {
width: 1600px;
height: 1600px;
-webkit-transform-origin: 0 0;
}
.leaflet-marker-icon,
.leaflet-marker-shadow {
display: block;
}
/* map is broken in FF if you have max-width: 100% on tiles */
.leaflet-container img {
/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */
/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */
.leaflet-container .leaflet-overlay-pane svg,
.leaflet-container .leaflet-marker-pane img,
.leaflet-container .leaflet-shadow-pane img,
.leaflet-container .leaflet-tile-pane img,
.leaflet-container img.leaflet-image-layer {
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-container.leaflet-touch-zoom {
-ms-touch-action: pan-x pan-y;
touch-action: pan-x pan-y;
}
.leaflet-container.leaflet-touch-drag {
-ms-touch-action: pinch-zoom;
}
.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom {
-ms-touch-action: none;
touch-action: none;
}
.leaflet-container {
-webkit-tap-highlight-color: transparent;
}
.leaflet-container a {
-webkit-tap-highlight-color: rgba(51, 181, 229, 0.4);
}
.leaflet-tile {
filter: inherit;
visibility: hidden;
@@ -52,18 +76,26 @@
.leaflet-zoom-box {
width: 0;
height: 0;
-moz-box-sizing: border-box;
box-sizing: border-box;
z-index: 800;
}
/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */
.leaflet-overlay-pane svg {
-moz-user-select: none;
}
.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; }
.leaflet-pane { z-index: 400; }
.leaflet-tile-pane { z-index: 200; }
.leaflet-overlay-pane { z-index: 400; }
.leaflet-shadow-pane { z-index: 500; }
.leaflet-marker-pane { z-index: 600; }
.leaflet-tooltip-pane { z-index: 650; }
.leaflet-popup-pane { z-index: 700; }
.leaflet-map-pane canvas { z-index: 100; }
.leaflet-map-pane svg { z-index: 200; }
.leaflet-vml-shape {
width: 1px;
@@ -80,7 +112,8 @@
.leaflet-control {
position: relative;
z-index: 7;
z-index: 800;
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
pointer-events: auto;
}
.leaflet-top,
@@ -124,7 +157,9 @@
/* zoom and fade animations */
.leaflet-fade-anim .leaflet-tile,
.leaflet-fade-anim .leaflet-tile {
will-change: opacity;
}
.leaflet-fade-anim .leaflet-popup {
opacity: 0;
-webkit-transition: opacity 0.2s linear;
@@ -132,11 +167,17 @@
-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-animated {
-webkit-transform-origin: 0 0;
-ms-transform-origin: 0 0;
transform-origin: 0 0;
}
.leaflet-zoom-anim .leaflet-zoom-animated {
will-change: transform;
}
.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);
@@ -144,8 +185,7 @@
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 {
.leaflet-pan-anim .leaflet-tile {
-webkit-transition: none;
-moz-transition: none;
-o-transition: none;
@@ -159,24 +199,44 @@
/* cursors */
.leaflet-clickable {
.leaflet-interactive {
cursor: pointer;
}
.leaflet-container {
.leaflet-grab {
cursor: -webkit-grab;
cursor: -moz-grab;
}
.leaflet-crosshair,
.leaflet-crosshair .leaflet-interactive {
cursor: crosshair;
}
.leaflet-popup-pane,
.leaflet-control {
cursor: auto;
}
.leaflet-dragging .leaflet-container,
.leaflet-dragging .leaflet-clickable {
.leaflet-dragging .leaflet-grab,
.leaflet-dragging .leaflet-grab .leaflet-interactive,
.leaflet-dragging .leaflet-marker-draggable {
cursor: move;
cursor: -webkit-grabbing;
cursor: -moz-grabbing;
}
/* marker & overlays interactivity */
.leaflet-marker-icon,
.leaflet-marker-shadow,
.leaflet-image-layer,
.leaflet-pane > svg path,
.leaflet-tile-container {
pointer-events: none;
}
.leaflet-marker-icon.leaflet-interactive,
.leaflet-image-layer.leaflet-interactive,
.leaflet-pane > svg path.leaflet-interactive {
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
pointer-events: auto;
}
/* visual tweaks */
@@ -249,7 +309,14 @@
height: 30px;
line-height: 30px;
}
.leaflet-touch .leaflet-bar a:first-child {
border-top-left-radius: 2px;
border-top-right-radius: 2px;
}
.leaflet-touch .leaflet-bar a:last-child {
border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px;
}
/* zoom control */
@@ -258,16 +325,10 @@
font: bold 18px 'Lucida Console', Monaco, monospace;
text-indent: 1px;
}
.leaflet-control-zoom-out {
font-size: 20px;
}
.leaflet-touch .leaflet-control-zoom-in {
.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out {
font-size: 22px;
}
.leaflet-touch .leaflet-control-zoom-out {
font-size: 24px;
}
/* layers control */
@@ -303,6 +364,11 @@
color: #333;
background: #fff;
}
.leaflet-control-layers-scrollbar {
overflow-y: scroll;
overflow-x: hidden;
padding-right: 5px;
}
.leaflet-control-layers-selector {
margin-top: 2px;
position: relative;
@@ -317,6 +383,11 @@
margin: 5px -10px 5px -6px;
}
/* Default icon URLs */
.leaflet-default-icon-path {
background-image: url(images/marker-icon.png);
}
/* attribution and scale controls */
@@ -354,8 +425,8 @@
font-size: 11px;
white-space: nowrap;
overflow: hidden;
-moz-box-sizing: content-box;
box-sizing: content-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
background: #fff;
background: rgba(255, 255, 255, 0.5);
@@ -386,6 +457,7 @@
.leaflet-popup {
position: absolute;
text-align: center;
margin-bottom: 20px;
}
.leaflet-popup-content-wrapper {
padding: 1px;
@@ -400,11 +472,13 @@
margin: 18px 0;
}
.leaflet-popup-tip-container {
margin: 0 auto;
width: 40px;
height: 20px;
position: relative;
position: absolute;
left: 50%;
margin-left: -20px;
overflow: hidden;
pointer-events: none;
}
.leaflet-popup-tip {
width: 17px;
@@ -422,7 +496,7 @@
.leaflet-popup-content-wrapper,
.leaflet-popup-tip {
background: white;
color: #333;
box-shadow: 0 3px 14px rgba(0,0,0,0.4);
}
.leaflet-container a.leaflet-popup-close-button {
@@ -430,6 +504,7 @@
top: 0;
right: 0;
padding: 4px 4px 0 0;
border: none;
text-align: center;
width: 18px;
height: 14px;
@@ -476,3 +551,82 @@
background: #fff;
border: 1px solid #666;
}
/* Tooltip */
/* Base styles for the element that has a tooltip */
.leaflet-tooltip {
position: absolute;
padding: 6px;
background-color: #fff;
border: 1px solid #fff;
border-radius: 3px;
color: #222;
white-space: nowrap;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
pointer-events: none;
box-shadow: 0 1px 3px rgba(0,0,0,0.4);
}
.leaflet-tooltip.leaflet-clickable {
cursor: pointer;
pointer-events: auto;
}
.leaflet-tooltip-top:before,
.leaflet-tooltip-bottom:before,
.leaflet-tooltip-left:before,
.leaflet-tooltip-right:before {
position: absolute;
pointer-events: none;
border: 6px solid transparent;
background: transparent;
content: "";
}
/* Directions */
.leaflet-tooltip-bottom {
margin-top: 6px;
}
.leaflet-tooltip-top {
margin-top: -6px;
}
.leaflet-tooltip-bottom:before,
.leaflet-tooltip-top:before {
left: 50%;
margin-left: -6px;
}
.leaflet-tooltip-top:before {
bottom: 0;
margin-bottom: -12px;
border-top-color: #fff;
}
.leaflet-tooltip-bottom:before {
top: 0;
margin-top: -12px;
margin-left: -6px;
border-bottom-color: #fff;
}
.leaflet-tooltip-left {
margin-left: -6px;
}
.leaflet-tooltip-right {
margin-left: 6px;
}
.leaflet-tooltip-left:before,
.leaflet-tooltip-right:before {
top: 50%;
margin-top: -6px;
}
.leaflet-tooltip-left:before {
right: 0;
margin-right: -12px;
border-left-color: #fff;
}
.leaflet-tooltip-right:before {
left: 0;
margin-left: -12px;
border-right-color: #fff;
}
+5 -9
View File
File diff suppressed because one or more lines are too long
-1
View File
@@ -1 +0,0 @@
../../lib/Leaflet.markercluster/dist/leaflet.markercluster-src.js
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -71,8 +71,8 @@ Add the ionicon stylesheet from a [CDN](http://code.ionicframework.com/ionicons/
| Property | Description | Default Value | Possible values |
| --------------- | ---------------------- | ------------- | ---------------------------------------------------- |
| icon | Name of the icon | 'home' | See glyphicons or font-awesome |
| prefix | Select de icon library | 'glyphicon' | 'fa' for font-awesome or 'glyphicon' for bootstrap 3 |
| markerColor | Color of the marker | 'blue' | 'red', 'darkred', 'orange', 'green', 'darkgreen', 'blue', 'purple', 'darkpuple', 'cadetblue' |
| prefix | Select the icon library| 'glyphicon' | 'fa' for font-awesome or 'glyphicon' for bootstrap 3 |
| markerColor | Color of the marker | 'blue' | 'red', 'darkred', 'orange', 'green', 'darkgreen', 'blue', 'purple', 'darkpurple', 'cadetblue' |
| iconColor | Color of the icon | 'white' | 'white', 'black' or css code (hex, rgba etc) |
| spin | Make the icon spin | false | true or false. Font-awesome required |
| extraClasses | Additional classes in the created <i> tag | '' | 'fa-rotate90 myclass' eller other custom configuration |
+18 -1
View File
@@ -1,3 +1,20 @@
sudo: false
language: node_js
node_js:
- 0.10
- 6
cache:
directories:
- "travis_phantomjs"
before_install:
- "export PHANTOMJS_VERSION=2.1.1"
- "hash -r"
- "phantomjs --version"
- "export PATH=$PWD/travis_phantomjs/phantomjs-$PHANTOMJS_VERSION-linux-x86_64/bin:$PATH"
- "hash -r"
- "phantomjs --version"
- "if [ $(phantomjs --version) != \"$PHANTOMJS_VERSION\" ]; then rm -rf $PWD/travis_phantomjs; mkdir -p $PWD/travis_phantomjs; fi"
- "hash -r"
- "if [ $(phantomjs --version) != \"$PHANTOMJS_VERSION\" ]; then wget https://github.com/Medium/phantomjs/releases/download/v$PHANTOMJS_VERSION/phantomjs-$PHANTOMJS_VERSION-linux-x86_64.tar.bz2 -O $PWD/travis_phantomjs/phantomjs-$PHANTOMJS_VERSION-linux-x86_64.tar.bz2; fi"
- "if [ $(phantomjs --version) != \"$PHANTOMJS_VERSION\" ]; then tar -xvf $PWD/travis_phantomjs/phantomjs-$PHANTOMJS_VERSION-linux-x86_64.tar.bz2 -C $PWD/travis_phantomjs; fi"
- "hash -r"
- "phantomjs --version"
+122 -3
View File
@@ -3,9 +3,127 @@ Leaflet.markercluster
(all changes without author notice are by [@danzel](https://github.com/danzel))
## Master
## 1.2.0 (2017-11-06)
##0.4 (2013-12-19)
### Improvements
* Move `clusterPane` option in to `options` field (by [@ghybs](https://github.com/ghybs)) [#832](https://github.com/Leaflet/Leaflet.markercluster/pull/832)
### Bugfixes
* Fix very small `maxClusterRadius` hanging the browser (by [@lucaswerkmeister](https://github.com/lucaswerkmeister)) [#838](https://github.com/Leaflet/Leaflet.markercluster/pull/838)
## 1.1.0 (2017-08-27)
### Improvements
* Add `clusterPane` option to allow putting clusters in a different pane (by [@ckrahe](https://github.com/ckrahe)) [#819](https://github.com/Leaflet/Leaflet.markercluster/issues/819)
## 1.0.6 (2017-06-19)
### Bugfixes
* Fix some issues when used with non-integer zoom [#789](https://github.com/Leaflet/Leaflet.markercluster/issues/789)
* Change examples to use https (by [@ghybs](https://github.com/ghybs)) [#794](https://github.com/Leaflet/Leaflet.markercluster/pull/794)
## 1.0.5 (2017-04-26)
### Improvements
* Allow passing fitBounds options to zoomToBounds (by [@timkelty](https://github.com/timkelty)) [#779](https://github.com/Leaflet/Leaflet.markercluster/pull/779)
### Bugfixes
* Fixed bug where disableClusteringAtZoom being 0 is treated the same as null (by [@MrCheeze](https://github.com/MrCheeze)) [#773](https://github.com/Leaflet/Leaflet.markercluster/pull/773)
## 1.0.4 (2017-03-14)
### Bugfixes
* Fix errors removing a MarkerClusterGroup from the map during an animation [#758](https://github.com/Leaflet/Leaflet.markercluster/issues/758)
## 1.0.3 (2017-02-02)
### Bugfixes
* Fix moving markers while the MarkerClusterGroup is not on the map [#753](https://github.com/Leaflet/Leaflet.markercluster/issues/753)
## 1.0.2 (2017-01-27)
### Improvements
* Support `layeradd` and `layerremove` events [#647](https://github.com/Leaflet/Leaflet.markercluster/issues/647)
### Bugfixes
* Add support for maps with negative minZoom [#704](https://github.com/Leaflet/Leaflet.markercluster/issues/704)
* Fixed zoomToShowLayer() markers disappearing bug (by [@z3ut](https://github.com/z3ut)) [#739](https://github.com/Leaflet/Leaflet.markercluster/issues/739)
* Fix an issue when opening a popup inside of zoomToShowLayer
* If a marker is moved with an open popup on it, re-open the popup after moving it. [#651](https://github.com/Leaflet/Leaflet.markercluster/issues/651)
## 1.0.1 (2017-01-25)
### Improvements
* Add install and build steps with jake (by [@kazes](https://github.com/kazes)) [#733](https://github.com/Leaflet/Leaflet.markercluster/pull/733)
* Readme improvements (by [@ghybs](https://github.com/ghybs), [@bertyhell](https://github.com/bertyhell)) [#734](https://github.com/Leaflet/Leaflet.markercluster/pull/738), [#734](https://github.com/Leaflet/Leaflet.markercluster/pull/738
* Bump all examples to leaflet 1.0.3
### Bugfixes
* Fixed leaflet 1.0.2 bug where clearLayers would throw an exception (by [@marcianoviereck92](https://github.com/marcianoviereck92)) [#746](https://github.com/Leaflet/Leaflet.markercluster/pull/746)
## 1.0.0 (2016-10-03)
### Improvements
* Compatibility with Leaflet 1.0.0 (by [@danzel](https://githum.com/danzel), [@Eschon](https://github.com/Eschon), [@ghybs](https://github.com/ghybs), [@IvanSanchez](https://github.com/IvanSanchez))
* Support moving markers [#57](https://github.com/Leaflet/Leaflet.markercluster/issues/57)
* chunkedLoading option to keep browser more responsive during larging a load data set [#292](https://github.com/Leaflet/Leaflet.markercluster/issues/292)
* maxClusterRadius can be a function (by [@Schwanksta](https://github.com/Schwanksta)) [#298](https://github.com/Leaflet/Leaflet.markercluster/issues/298)
* Spiderfy without zooming when all markers at same location (by [@rdenniston](https://github.com/rdenniston), [@ghybs](https://github.com/ghybs)) [#415](https://github.com/Leaflet/Leaflet.markercluster/issues/415), [#606](https://github.com/Leaflet/Leaflet.markercluster/issues/606)
* On becoming visible, markers retain their original opacity. (by [@IvanSanchez](https://github.com/IvanSanchez)) [#444](https://github.com/Leaflet/Leaflet.markercluster/issues/444)
* Spiderleg Polyline options (by [@mikeatlas](https://github.com/mikeatlas)) [#466](https://github.com/Leaflet/Leaflet.markercluster/issues/466)
* Extra methods to allow refreshing cluster icons (by [@ghybs](https://github.com/ghybs)) [#564](https://github.com/Leaflet/Leaflet.markercluster/issues/564)
* Ability to disable animations (by [@ghybs](https://github.com/ghybs)) [#578](https://github.com/Leaflet/Leaflet.markercluster/issues/578)
* Optimized performance of bulk addLayers and removeLayers (by [@ghybs](https://github.com/ghybs)) [#584](https://github.com/Leaflet/Leaflet.markercluster/issues/584)
* Replaced spiderfy legs animation from SMIL to CSS transition (by [@ghybs](https://github.com/ghybs)) [#585](https://github.com/Leaflet/Leaflet.markercluster/issues/585)
* Provide more detailed context information on the spiderfied event (by [@evanvosberg](https://github.com/evanvosberg)) [#421](https://github.com/Leaflet/Leaflet.markercluster/issues/421)
* Add unspiderfied event
* Readme updates (by [@ghybs](https://github.com/ghybs), [@tomchadwin](https://github.com/tomchadwin) [@Cyrille37](https://github.com/Cyrille37) [@franckl](https://github.com/franckl) [@mikeatlas](https://github.com/mikeatlas)
[@rdenniston](https://github.com/rdenniston) [@maackle](https://github.com/maackle) [@fureigh](https://github.com/fureigh) [@Wildhoney](https://github.com/Wildhoney) [@Schwanksta](https://github.com/Schwanksta) [@frankrowe](https://github.com/frankrowe))
* Improve adding and removing nested LayerGroups (by [@ghybs](https://github.com/ghybs)) [#624](https://github.com/Leaflet/Leaflet.markercluster/pull/624)
* Add public unspiderfy method (by [@zverev](https://github.com/zverev)) [#617](https://github.com/Leaflet/Leaflet.markercluster/pull/617)
* Optimized performance of bulk add with complex icon create function (by [@mlazowik](https://github.com/mlazowik)) [#697](https://github.com/Leaflet/Leaflet.markercluster/pull/697)
* Remove leaflet from peerDependencies (by [@tyleralves](https://github.com/tyleralves)) [#703](https://github.com/Leaflet/Leaflet.markercluster/pull/703)
* Simplified _recursively (by [@ghybs](https://github.com/ghybs)) [#656](https://github.com/Leaflet/Leaflet.markercluster/pull/656)
### Bugfixes
* Fix getBounds when removeOutsideVisibleBounds: false is set. [#321](https://github.com/Leaflet/Leaflet.markercluster/issues/321)
* Fix zoomToShowLayer fails after initial spiderfy [#286](https://github.com/Leaflet/Leaflet.markercluster/issues/286)
* Fix cluster not disappearing on Android [#344](https://github.com/Leaflet/Leaflet.markercluster/issues/344)
* Fix RemoveLayers() when spiderified (by [@Grsmto](https://github.com/Grsmto)) [#358](https://github.com/Leaflet/Leaflet.markercluster/issues/358)
* Remove lines from map when removing cluster (by [@olive380](https://github.com/olive380)) [#532](https://github.com/Leaflet/Leaflet.markercluster/issues/532)
* Fix getConvexHull when all markers are located at same latitude (by [@olive380](https://github.com/olive380)) [#533](https://github.com/Leaflet/Leaflet.markercluster/issues/533)
* Fix removeLayers when cluster is not on the map (by [@eschon](https://github.com/eschon)) [#556](https://github.com/Leaflet/Leaflet.markercluster/issues/556)
* Improved zoomToShowLayer with callback check (by [@ghybs](https://github.com/ghybs)) [#572](https://github.com/Leaflet/Leaflet.markercluster/issues/572)
* Improved reliability of RefreshSpec test suite for PhantomJS (by [@ghybs](https://github.com/ghybs)) [#577](https://github.com/Leaflet/Leaflet.markercluster/issues/577)
* Corrected effect of removeOutsideVisibleBounds option (by [@ghybs](https://github.com/ghybs)) [#575](https://github.com/Leaflet/Leaflet.markercluster/issues/575)
* Fix getLayer when provided a string [#531](https://github.com/Leaflet/Leaflet.markercluster/issues/531)
* Documentation improvements (by [@ghybs](https://github.com/ghybs)) [#579](https://github.com/Leaflet/Leaflet.markercluster/issues/579)
* Correct _getExpandedVisibleBounds for Max Latitude (by [@ghybs](https://github.com/ghybs)) [#587](https://github.com/Leaflet/Leaflet.markercluster/issues/587)
* Correct unspiderfy vector (by [@ghybs](https://github.com/ghybs)) [#604](https://github.com/Leaflet/Leaflet.markercluster/issues/604)
* Remove "leaflet-cluster-anim" class on map remove while spiderfied (by [@ghybs](https://github.com/ghybs)) [#607](https://github.com/Leaflet/Leaflet.markercluster/issues/607)
* Fix disableClusteringAtZoom maxZoom troubles (by [@OriginalSin](https://github.com/OriginalSin)) [#609](https://github.com/Leaflet/Leaflet.markercluster/issues/609)
* Fix clusters not disappearing when they were near the edge on mobile (by [@ghybs](https://github.com/ghybs)) [#529](https://github.com/Leaflet/Leaflet.markercluster/issues/529)
* Remove leaflet from dependencies (by [@ghybs](https://github.com/ghybs)) [#639](https://github.com/Leaflet/Leaflet.markercluster/issues/639)
* Fix interaction between zoomOrSpiderfy and disableClusteringAtZoom (by [@ghybs](https://github.com/ghybs)) [#633](https://github.com/Leaflet/Leaflet.markercluster/issues/633) [#648](https://github.com/Leaflet/Leaflet.markercluster/issues/648)
## 0.4 (2013-12-19)
### Improvements
@@ -16,7 +134,8 @@ Leaflet.markercluster
* Fix some cases zoomToShowLayer wouldn't work (Reported by [@absemetov](https://github.com/absemetov)) [#203](https://github.com/Leaflet/Leaflet.markercluster/issues/203) [#228](https://github.com/Leaflet/Leaflet.markercluster/issues/228) [#286](https://github.com/Leaflet/Leaflet.markercluster/issues/286)
##0.3 (2013-12-18)
## 0.3 (2013-12-18)
### Improvements
+70
View File
@@ -0,0 +1,70 @@
Contributing to Leaflet.MarkerCluster
=====================================
1. [Reporting Bugs](#reporting-bugs)
2. [Contributing Code](#contributing-code)
3. [Building](#building)
4. [Testing](#testing)
## Reporting Bugs
Before reporting a bug on the project's [issues page](https://github.com/Leaflet/Leaflet.markercluster/issues),
first make sure that your issue is caused by Leaflet.MarkerCluster, not your application code
(e.g. passing incorrect arguments to methods, etc.).
Second, search the already reported issues for similar cases,
and if it's already reported, just add any additional details in the comments.
After you've made sure that you've found a new Leaflet.markercluster bug,
here are some tips for creating a helpful report that will make fixing it much easier and quicker:
* Write a **descriptive, specific title**. Bad: *Problem with polylines*. Good: *Doing X in IE9 causes Z*.
* Include **browser, OS and Leaflet version** info in the description.
* Create a **simple test case** that demonstrates the bug (e.g. using [JSFiddle](http://jsfiddle.net/) or [JS Bin](http://jsbin.com/)).
* Check whether the bug can be reproduced in **other browsers**.
* Check if the bug occurs in the stable version, master, or both.
* *Bonus tip:* if the bug only appears in the master version but the stable version is fine,
use `git bisect` to find the exact commit that introduced the bug.
If you just want some help with your project,
try asking [on the Leaflet forum](https://groups.google.com/forum/#!forum/leaflet-js) instead.
## Contributing Code
### Considerations for Accepting Patches
While we happily accept patches, we're also committed to keeping Leaflet simple, lightweight and blazingly fast.
So bugfixes, performance optimizations and small improvements that don't add a lot of code
are much more likely to get accepted quickly.
Before sending a pull request with a new feature, check if it's been discussed before already
(either on [GitHub issues](https://github.com/Leaflet/Leaflet/issues)
or [Leaflet UserVoice](http://leaflet.uservoice.com/)),
and ask yourself two questions:
1. Are you sure that this new feature is important enough to justify its presence in the Leaflet core?
Or will it look better as a plugin in a separate repository?
2. Is it written in a simple, concise way that doesn't add bulk to the codebase?
If your feature or API improvement did get merged into master,
please consider submitting another pull request with the corresponding [documentation update](#improving-documentation).
## Building
Install the dependencies:
```
npm install -g jake
npm install
```
Then to build:
```
jake
```
Output will be in the ```dist/``` directory
## Testing
To run unit tests:
```
jake test
```
@@ -0,0 +1,23 @@
- [ ] I'm reporting a bug, not asking for help
- [ ] I'm sure this is a Leaflet.MarkerCluster code issue, not an issue with my own code nor with the framework I'm using (Cordova, Ionic, Angular, React…)
- [ ] I've searched through the issues to make sure it's not yet reported
----
## How to reproduce
- Leaflet version I'm using:
- Leaflet.MarkerCluster version I'm using:
- Browser (with version) I'm using:
- OS/Platform (with version) I'm using:
- step 1
- step 2
## What behaviour I'm expecting and which behaviour I'm seeing
## Minimal example reproducing the issue
- [ ] this example is as simple as possible
- [ ] this example does not rely on any third party code
Using http://playground-leaflet.rhcloud.com/beg/1/edit?html,output or any other jsfiddle like site.
+204 -62
View File
@@ -3,77 +3,148 @@ Leaflet.markercluster
Provides Beautiful Animated Marker Clustering functionality for [Leaflet](http://leafletjs.com), a JS library for interactive maps.
*Requires Leaflet 0.7.0 or newer.*
*Requires Leaflet 1.0.0*
![cluster map example](example/map.png)
For a Leaflet 0.7 compatible version, [use the leaflet-0.7 branch](https://github.com/Leaflet/Leaflet.markercluster/tree/leaflet-0.7)<br>
For a Leaflet 0.5 compatible version, [Download b128e950](https://github.com/Leaflet/Leaflet.markercluster/archive/b128e950d8f5d7da5b60bd0aa9a88f6d3dd17c98.zip)<br>
For a Leaflet 0.4 compatible version, [Download the 0.2 release](https://github.com/Leaflet/Leaflet.markercluster/archive/0.2.zip)
<!---
TOC created with gh-md-toc
https://github.com/ekalinin/github-markdown-toc
removed link to h1 and indented back 2 spaces all links.
-->
## Table of Contents
* [Using the plugin](#using-the-plugin)
* [Building, testing and linting scripts](#building-testing-and-linting-scripts)
* [Examples](#examples)
* [Usage](#usage)
* [Options](#options)
* [Defaults](#defaults)
* [Customising the Clustered Markers](#customising-the-clustered-markers)
* [All Options](#all-options)
* [Enabled by default (boolean options)](#enabled-by-default-boolean-options)
* [Other options](#other-options)
* [Chunked addLayers options](#chunked-addlayers-options)
* [Events](#events)
* [Additional MarkerClusterGroup Events](#additional-markerclustergroup-events)
* [Methods](#methods)
* [Group methods](#group-methods)
* [Adding and removing Markers](#adding-and-removing-markers)
* [Bulk adding and removing Markers](#bulk-adding-and-removing-markers)
* [Getting the visible parent of a marker](#getting-the-visible-parent-of-a-marker)
* [Refreshing the clusters icon](#refreshing-the-clusters-icon)
* [Other Group Methods](#other-group-methods)
* [Clusters methods](#clusters-methods)
* [Getting the bounds of a cluster](#getting-the-bounds-of-a-cluster)
* [Zooming to the bounds of a cluster](#zooming-to-the-bounds-of-a-cluster)
* [Other clusters methods](#other-clusters-methods)
* [Handling LOTS of markers](#handling-lots-of-markers)
* [License](#license)
* [Sub-plugins](#sub-plugins)
## Using the plugin
Include the plugin CSS and JS files on your page after Leaflet files, using your method of choice:
* [Download the `v1.2.0` release](https://github.com/Leaflet/Leaflet.markercluster/archive/v1.2.0.zip)
* Use unpkg CDN: `https://unpkg.com/[email protected]/dist/`
* Install with npm: `npm install leaflet.markercluster`
Install with Bower: `bower install leaflet.markercluster`
In each case, use files in the `dist` folder:
* `MarkerCluster.css`
* `MarkerCluster.Default.css` (not needed if you use your own `iconCreateFunction` instead of the default one)
* `leaflet.markercluster.js` (or `leaflet.markercluster-src.js` for the non-minified version)
### Building, testing and linting scripts
Install jake `npm install -g jake` then run `npm install`
* To check the code for errors and build Leaflet from source, run `jake`.
* To run the tests, run `jake test`.
### Examples
See the included examples for usage.
The [realworld example](http://leaflet.github.com/Leaflet.markercluster/example/marker-clustering-realworld.388.html) is a good place to start, it uses all of the defaults of the clusterer.
Or check out the [custom example](http://leaflet.github.com/Leaflet.markercluster/example/marker-clustering-custom.html) for how to customise the behaviour and appearance of the clusterer
The [realworld example](https://leaflet.github.io/Leaflet.markercluster/example/marker-clustering-realworld.388.html) is a good place to start, it uses all of the defaults of the clusterer.
Or check out the [custom example](https://leaflet.github.io/Leaflet.markercluster/example/marker-clustering-custom.html) for how to customise the behaviour and appearance of the clusterer
### Usage
Create a new MarkerClusterGroup, add your markers to it, then add it to the map
```javascript
var markers = new L.MarkerClusterGroup();
markers.addLayer(new L.Marker(getRandomLatLng(map)));
var markers = L.markerClusterGroup();
markers.addLayer(L.marker(getRandomLatLng(map)));
... Add more layers ...
map.addLayer(markers);
```
## Options
### Defaults
By default the Clusterer enables some nice defaults for you:
* **showCoverageOnHover**: When you mouse over a cluster it shows the bounds of its markers.
* **zoomToBoundsOnClick**: When you click a cluster we zoom to its bounds.
* **spiderfyOnMaxZoom**: When you click a cluster at the bottom zoom level we spiderfy it so you can see all of its markers. (*Note: the spiderfy occurs at the current zoom level if all items within the cluster are physically located at the same latitude and longitude.*)
* **spiderfyOnMaxZoom**: When you click a cluster at the bottom zoom level we spiderfy it so you can see all of its markers. (*Note: the spiderfy occurs at the current zoom level if all items within the cluster are still clustered at the maximum zoom level or at zoom specified by `disableClusteringAtZoom` option*)
* **removeOutsideVisibleBounds**: Clusters and markers too far from the viewport are removed from the map for performance.
* **spiderLegPolylineOptions**: Allows you to specify [PolylineOptions](http://leafletjs.com/reference.html#polyline-options) to style spider legs. By default, they are `{ weight: 1.5, color: '#222' }`.
* **spiderLegPolylineOptions**: Allows you to specify [PolylineOptions](http://leafletjs.com/reference.html#polyline-options) to style spider legs. By default, they are `{ weight: 1.5, color: '#222', opacity: 0.5 }`.
You can disable any of these as you want in the options when you create the MarkerClusterGroup:
```javascript
var markers = new L.MarkerClusterGroup({ spiderfyOnMaxZoom: false, showCoverageOnHover: false, zoomToBoundsOnClick: false });
var markers = L.markerClusterGroup({
spiderfyOnMaxZoom: false,
showCoverageOnHover: false,
zoomToBoundsOnClick: false
});
```
### Customising the Clustered Markers
As an option to MarkerClusterGroup you can provide your own function for creating the Icon for the clustered markers.
The default implementation changes color at bounds of 10 and 100, but more advanced uses may require customising this.
You do not need to include the .Default css if you go this way.
You are passed a MarkerCluster object, you'll probably want to use getChildCount() or getAllChildMarkers() to work out the icon to show
You are passed a MarkerCluster object, you'll probably want to use `getChildCount()` or `getAllChildMarkers()` to work out the icon to show.
```javascript
var markers = new L.MarkerClusterGroup({
var markers = L.markerClusterGroup({
iconCreateFunction: function(cluster) {
return new L.DivIcon({ html: '<b>' + cluster.getChildCount() + '</b>' });
return L.divIcon({ html: '<b>' + cluster.getChildCount() + '</b>' });
}
});
```
Check out the [custom example](http://leaflet.github.com/Leaflet.markercluster/example/marker-clustering-custom.html) for an example of this.
Check out the [custom example](https://leaflet.github.io/Leaflet.markercluster/example/marker-clustering-custom.html) for an example of this.
If you need to update the clusters icon (e.g. they are based on markers real-time data), use the method [refreshClusters()](#refreshing-the-clusters-icon).
### All Options
Enabled by default (boolean options):
#### Enabled by default (boolean options)
* **showCoverageOnHover**: When you mouse over a cluster it shows the bounds of its markers.
* **zoomToBoundsOnClick**: When you click a cluster we zoom to its bounds.
* **spiderfyOnMaxZoom**: When you click a cluster at the bottom zoom level we spiderfy it so you can see all of its markers. (*Note: the spiderfy occurs at the current zoom level if all items within the cluster are physically located at the same latitude and longitude.*)
* **spiderfyOnMaxZoom**: When you click a cluster at the bottom zoom level we spiderfy it so you can see all of its markers. (*Note: the spiderfy occurs at the current zoom level if all items within the cluster are still clustered at the maximum zoom level or at zoom specified by `disableClusteringAtZoom` option*).
* **removeOutsideVisibleBounds**: Clusters and markers too far from the viewport are removed from the map for performance.
* **animate**: Smoothly split / merge cluster children when zooming and spiderfying. If `L.DomUtil.TRANSITION` is false, this option has no effect (no animation is possible).
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. addLayers does not support this, only addLayer with individual 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)
#### Other options
* **animateAddingMarkers**: If set to true (and `animate` option is also true) then adding individual markers to the MarkerClusterGroup after it has been added to the map will add the marker and animate it into the cluster. Defaults to false as this gives better performance when bulk adding markers. addLayers does not support this, only addLayer with individual Markers.
* **disableClusteringAtZoom**: If set, at this zoom level and below, markers will not be clustered. This defaults to disabled. [See Example](https://leaflet.github.io/Leaflet.markercluster/example/marker-clustering-realworld-maxzoom.388.html). Note: you may be interested in disabling `spiderfyOnMaxZoom` option when using `disableClusteringAtZoom`.
* **maxClusterRadius**: The maximum radius that a cluster will cover from the central marker (in pixels). Default 80. Decreasing will make more, smaller clusters. You can also use a function that accepts the current map zoom and returns the maximum cluster radius in pixels.
* **polygonOptions**: Options to pass when creating the L.Polygon(points, options) 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 (Default:1)
* **iconCreateFunction**: Function used to create the cluster icon [See default as example](https://github.com/Leaflet/Leaflet.markercluster/blob/15ed12654acdc54a4521789c498e4603fe4bf781/src/MarkerClusterGroup.js#L542).
* **polygonOptions**: Options to pass when creating the L.Polygon(points, options) to show the bounds of a cluster. Defaults to empty, which lets Leaflet use the [default Path options](http://leafletjs.com/reference.html#path-options).
* **singleMarkerMode**: If set to true, overrides the icon for all added markers to make them appear as a 1 size cluster. Note: the markers are not replaced by cluster objects, only their icon is replaced. Hence they still react to normal events, and option `disableClusteringAtZoom` does not restore their previous icon (see [#391](https://github.com/Leaflet/Leaflet.markercluster/issues/391)).
* **spiderLegPolylineOptions**: Allows you to specify [PolylineOptions](http://leafletjs.com/reference.html#polyline-options) to style spider legs. By default, they are `{ weight: 1.5, color: '#222', opacity: 0.5 }`.
* **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 (Default: 1).
* **iconCreateFunction**: Function used to create the cluster icon. See [the default implementation](https://github.com/Leaflet/Leaflet.markercluster/blob/15ed12654acdc54a4521789c498e4603fe4bf781/src/MarkerClusterGroup.js#L542) or the [custom example](https://leaflet.github.io/Leaflet.markercluster/example/marker-clustering-custom.html).
* **clusterPane**: Map pane where the cluster icons will be added. Defaults to L.Marker's default (currently 'markerPane'). [See the pane example](https://leaflet.github.io/Leaflet.markercluster/example/marker-clustering-pane.html).
#### Chunked addLayers options
Options for the [addLayers](#bulk-adding-and-removing-markers) method. See [#357](https://github.com/Leaflet/Leaflet.markercluster/issues/357) for explanation on how the chunking works.
* **chunkedLoading**: Boolean to split the addLayer**s** processing in to small intervals so that the page does not freeze.
* **chunkInterval**: Time interval (in ms) during which addLayers works before pausing to let the rest of the page process. In particular, this prevents the page from freezing while adding a lot of markers. Defaults to 200ms.
* **chunkDelay**: Time delay (in ms) between consecutive periods of processing for addLayers. Default to 50ms.
* **chunkProgress**: Callback function that is called at the end of each chunkInterval. Typically used to implement a progress indicator, e.g. [code in RealWorld 50k](https://github.com/Leaflet/Leaflet.markercluster/blob/master/example/marker-clustering-realworld.50000.html#L33-L49). Defaults to null. Arguments:
1. Number of processed markers
2. Total number of markers being added
3. Elapsed time (in ms)
## Events
If you register for click, mouseover, etc events are just related to Markers in the cluster.
To receive events for clusters listen to 'cluster' + 'eventIWant', ex: 'clusterclick', 'clustermouseover'.
Leaflet events like `click`, `mouseover`, etc. are just related to _Markers_ in the cluster.
To receive events for clusters, listen to `'cluster' + '<eventName>'`, ex: `clusterclick`, `clustermouseover`, `clustermouseout`.
Set your callback up as follows to handle both cases:
@@ -83,68 +154,139 @@ markers.on('click', function (a) {
});
markers.on('clusterclick', function (a) {
// a.layer is actually a cluster
console.log('cluster ' + a.layer.getAllChildMarkers().length);
});
```
#### Additional MarkerClusterGroup Events
### Additional MarkerClusterGroup Events
- **animationend**: Fires when marker clustering/unclustering animation has completed
- **spiderfied**: Fires when overlapping markers get spiderified
- **spiderfied**: Fires when overlapping markers get spiderified (Contains ```cluster``` and ```markers``` attributes)
- **unspiderfied**: Fires when overlapping markers get unspiderified (Contains ```cluster``` and ```markers``` attributes)
## Methods
### Getting the bounds of a cluster
When you receive 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()));
});
```
### Group methods
### Zooming to the bounds of a cluster
When you receive an event from a cluster you can zoom to its bounds in one easy step.
If all of the markers will appear at a higher zoom level, that zoom level is zoomed to instead.
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.
### Getting the visible parent of a marker
#### 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. You can use [dedicated options](#chunked-addlayers-options) to fine-tune the behaviour of `addLayers`.
These methods extract non-group layer children from Layer Group types, even deeply nested. _However_, be noted that:
- `chunkProgress` jumps backward when `addLayers` finds a group (since appending its children to the input array makes the total increase).
- Groups are not actually added into the MarkerClusterGroup, only their non-group child layers. Therfore, `hasLayer` method will return `true` for non-group child layers, but `false` on any (possibly parent) Layer Group types.
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.
#### Getting the visible parent of a marker
If you have a marker in your MarkerClusterGroup and you want to get the visible parent of it (Either itself or a cluster it is contained in that is currently visible on the map).
This will return null if the marker and its parent clusters are not visible currently (they are not near the visible viewpoint)
```
```javascript
var visibleOne = markerClusterGroup.getVisibleParent(myMarker);
console.log(visibleOne.getLatLng());
```
### Adding and removing Markers
addLayer, removeLayer and clearLayers are supported and they should work for most uses.
#### Refreshing the clusters icon
If you have [customized](#customising-the-clustered-markers) the clusters icon to use some data from the contained markers, and later that data changes, use this method to force a refresh of the cluster icons.
You can use the method:
- without arguments to force all cluster icons in the Marker Cluster Group to be re-drawn.
- with an array or a mapping of markers to force only their parent clusters to be re-drawn.
- with an L.LayerGroup. The method will look for all markers in it. Make sure it contains only markers which are also within this Marker Cluster Group.
- with a single marker.
```javascript
markers.refreshClusters();
markers.refreshClusters([myMarker0, myMarker33]);
markers.refreshClusters({id_0: myMarker0, id_any: myMarker33});
markers.refreshClusters(myLayerGroup);
markers.refreshClusters(myMarker);
```
### 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
The plugin also adds a method on L.Marker to easily update the underlying icon options and refresh the icon.
If passing a second argument that evaluates to `true`, the method will also trigger a `refreshCluster` on the parent MarkerClusterGroup for that single marker.
```javascript
// Use as many times as required to update markers,
// then call refreshClusters once finished.
for (i in markersSubArray) {
markersSubArray[i].refreshIconOptions(newOptionsMappingArray[i]);
}
markers.refreshClusters(markersSubArray);
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.
// If updating only one marker, pass true to
// refresh this marker's parent clusters right away.
myMarker.refreshIconOptions(optionsMap, true);
```
### 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.
````
#### Other Group Methods
* **hasLayer**(layer): Returns true if the given layer (marker) is in the MarkerClusterGroup.
* **zoomToShowLayer**(layer, callback): Zooms to show the given marker (spiderfying if required), calls the callback when the marker is visible on the map.
### Clusters methods
The following methods can be used with clusters (not the group). They are typically used for event handling.
#### Getting the bounds of a cluster
When you receive an event from a cluster you can query it for the bounds.
```javascript
markers.on('clusterclick', function (a) {
var latLngBounds = a.layer.getBounds();
});
```
You can also query for the bounding convex polygon.
See [example/marker-clustering-convexhull.html](https://leaflet.github.io/Leaflet.markercluster/example/marker-clustering-convexhull.html) for a working example.
```javascript
markers.on('clusterclick', function (a) {
map.addLayer(L.polygon(a.layer.getConvexHull()));
});
```
#### Zooming to the bounds of a cluster
When you receive an event from a cluster you can zoom to its bounds in one easy step.
If all of the markers will appear at a higher zoom level, that zoom level is zoomed to instead.
`zoomToBounds` takes an optional argument to pass [options to the resulting `fitBounds` call](http://leafletjs.com/reference.html#map-fitboundsoptions).
See [marker-clustering-zoomtobounds.html](https://leaflet.github.io/Leaflet.markercluster/example/marker-clustering-zoomtobounds.html) for a working example.
```javascript
markers.on('clusterclick', function (a) {
a.layer.zoomToBounds({padding: [20, 20]});
});
```
#### Other clusters methods
* **getChildCount**: Returns the total number of markers contained within that cluster.
* **getAllChildMarkers**: Returns the array of total markers contained within that cluster.
* **spiderfy**: Spiderfies the child markers of this cluster
* **unspiderfy**: Unspiderfies a cluster (opposite of spiderfy)
## 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)
The Clusterer can handle 10,000 or even 50,000 markers (in chrome). IE9 has some issues with 50,000.
- [realworld 10,000 example](https://leaflet.github.io/Leaflet.markercluster/example/marker-clustering-realworld.10000.html)
- [realworld 50,000 example](https://leaflet.github.io/Leaflet.markercluster/example/marker-clustering-realworld.50000.html)
### License
Note: these two examples use the `chunkedLoading` option set to true in order to avoid locking the browser for a long time.
## License
Leaflet.markercluster is free software, and may be redistributed under the MIT-LICENSE.
[![Build Status](https://travis-ci.org/Leaflet/Leaflet.markercluster.png?branch=master)](https://travis-ci.org/Leaflet/Leaflet.markercluster)
## Sub-plugins
Leaflet.markercluster plugin is very popular and as such it generates high and
diverse expectations for increased functionalities.
If you are in that case, be sure to have a look first at the repository
[issues](https://github.com/Leaflet/Leaflet.markercluster/issues) in case what
you are looking for would already be discussed, and some workarounds would be proposed.
Check also the below sub-plugins:
| Plugin | Description | Maintainer |
| :----- | :---------- | :--------- |
| [Leaflet.FeatureGroup.SubGroup](https://github.com/ghybs/Leaflet.FeatureGroup.SubGroup) | Creates a Feature Group that adds its child layers into a parent group when added to a map (e.g. through L.Control.Layers). Typical usage is to dynamically add/remove groups of markers from Marker Cluster. | [ghybs](https://github.com/ghybs) |
| [Leaflet.MarkerCluster.LayerSupport](https://github.com/ghybs/Leaflet.MarkerCluster.LayerSupport) | Brings compatibility with L.Control.Layers and other Leaflet plugins. I.e. everything that uses direct calls to map.addLayer and map.removeLayer. | [ghybs](https://github.com/ghybs) |
| [Leaflet.MarkerCluster.Freezable](https://github.com/ghybs/Leaflet.MarkerCluster.Freezable) | Adds the ability to freeze clusters at a specified zoom. E.g. freezing at maxZoom + 1 makes as if clustering was programmatically disabled. | [ghybs](https://github.com/ghybs) |
| [Leaflet.MarkerCluster.PlacementStrategies](https://github.com/adammertel/Leaflet.MarkerCluster.PlacementStrategies) | Implements new strategies to position clustered markers (eg: clock, concentric circles, ...). Recommended to use with circleMarkers. [Demo](https://adammertel.github.io/Leaflet.MarkerCluster.PlacementStrategies/demo/demo1.html) | [adammertel](https://github.com/adammertel) / [UNIVIE](http://carto.univie.ac.at/) |
| [Leaflet.MarkerCluster.List](https://github.com/adammertel/Leaflet.MarkerCluster.List) | Displays child elements in a list. Suitable for mobile devices. [Demo](https://adammertel.github.io/Leaflet.MarkerCluster.List/demo/demo1.html) | [adammertel](https://github.com/adammertel) / [UNIVIE](http://carto.univie.ac.at/) |
+2 -3
View File
@@ -1,7 +1,6 @@
{
"name": "leaflet.markercluster",
"version": "0.4.0",
"main": "dist/leaflet.markercluster-src.js",
"version": "1.2.0",
"homepage": "https://github.com/Leaflet/Leaflet.markercluster",
"authors": [
"Dave Leaver <[email protected]>"
@@ -23,6 +22,6 @@
"tests"
],
"dependencies": {
"leaflet": ">= 0.7.0"
"leaflet": ">= 1.0.3"
}
}
+6
View File
@@ -18,6 +18,12 @@ var deps = {
src: ['MarkerCluster.Spiderfier.js'],
desc: 'Provides the ability to show all of the child markers of a cluster.',
heading: 'Spiderfier'
},
Refresh: {
src: ['MarkerClusterGroup.Refresh.js'],
desc: 'Method to request refreshing of clusters icon to reflect changes in markers data.',
heading: 'Refresh'
}
};
+9 -1
View File
@@ -3,4 +3,12 @@
-moz-transition: -moz-transform 0.3s ease-out, opacity 0.3s ease-in;
-o-transition: -o-transform 0.3s ease-out, opacity 0.3s ease-in;
transition: transform 0.3s ease-out, opacity 0.3s ease-in;
}
}
.leaflet-cluster-spider-leg {
/* stroke-dashoffset (duration and function) should match with leaflet-marker-icon transform in order to track it exactly */
-webkit-transition: -webkit-stroke-dashoffset 0.3s ease-out, -webkit-stroke-opacity 0.3s ease-in;
-moz-transition: -moz-stroke-dashoffset 0.3s ease-out, -moz-stroke-opacity 0.3s ease-in;
-o-transition: -o-stroke-dashoffset 0.3s ease-out, -o-stroke-opacity 0.3s ease-in;
transition: stroke-dashoffset 0.3s ease-out, stroke-opacity 0.3s ease-in;
}
@@ -0,0 +1,5 @@
We don't ship the .js files in the git master branch.
They are only present in version tags and in npm.
See how to get the JS files here: https://github.com/Leaflet/Leaflet.markercluster#using-the-plugin
Or how to build them: https://github.com/Leaflet/Leaflet.markercluster#building-testing-and-linting-scripts
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
@@ -3,8 +3,8 @@
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7/leaflet.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ==" crossorigin="" />
<script src="https://unpkg.com/[email protected]/dist/leaflet-src.js" integrity="sha512-WXoSHqw/t26DszhdMhOXOkI7qCiv5QWXhH9R7CgvgZMHz1ImlkVQ3uNsiQKu5wwbbxtPzFXd1hK4tzno2VqhpA==" crossorigin=""></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="screen.css" />
@@ -30,9 +30,9 @@
]
};
var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
var tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
});
var map = L.map('map')
Binary file not shown.

After

Width:  |  Height:  |  Size: 381 KiB

@@ -3,8 +3,8 @@
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7/leaflet.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ==" crossorigin="" />
<script src="https://unpkg.com/[email protected]/dist/leaflet-src.js" integrity="sha512-WXoSHqw/t26DszhdMhOXOkI7qCiv5QWXhH9R7CgvgZMHz1ImlkVQ3uNsiQKu5wwbbxtPzFXd1hK4tzno2VqhpA==" crossorigin=""></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="screen.css" />
@@ -20,9 +20,9 @@
<script type="text/javascript">
var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
var tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}),
latlng = L.latLng(50.5, 30.51);
@@ -3,8 +3,8 @@
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7/leaflet.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ==" crossorigin="" />
<script src="https://unpkg.com/[email protected]/dist/leaflet-src.js" integrity="sha512-WXoSHqw/t26DszhdMhOXOkI7qCiv5QWXhH9R7CgvgZMHz1ImlkVQ3uNsiQKu5wwbbxtPzFXd1hK4tzno2VqhpA==" crossorigin=""></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="screen.css" />
@@ -28,9 +28,9 @@
<script type="text/javascript">
var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
var tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}),
latlng = L.latLng(50.5, 30.51);
@@ -0,0 +1,83 @@
<!DOCTYPE html>
<html>
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ==" crossorigin="" />
<script src="https://unpkg.com/[email protected]/dist/leaflet-src.js" integrity="sha512-WXoSHqw/t26DszhdMhOXOkI7qCiv5QWXhH9R7CgvgZMHz1ImlkVQ3uNsiQKu5wwbbxtPzFXd1hK4tzno2VqhpA==" crossorigin=""></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="screen.css" />
<link rel="stylesheet" href="../dist/MarkerCluster.css" />
<link rel="stylesheet" href="../dist/MarkerCluster.Default.css" />
<script src="../dist/leaflet.markercluster-src.js"></script>
</head>
<body>
<div id="map"></div>
<button id="moveone">Move a random marker</button>
<script type="text/javascript">
var tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}),
latlng = new L.LatLng(50.5, 30.51);
var map = new L.Map('map', {center: latlng, zoom: 15, layers: [tiles]});
var markers = new L.MarkerClusterGroup();
var markersList = [];
function populate() {
for (var i = 0; i < 100; i++) {
var m = new L.Marker(getRandomLatLng(map), { draggable: true });
markersList.push(m);
markers.addLayer(m);
}
return false;
}
function populateRandomVector() {
for (var i = 0, latlngs = [], len = 20; i < len; i++) {
latlngs.push(getRandomLatLng(map));
}
var path = new L.Polyline(latlngs);
map.addLayer(path);
}
function getRandomLatLng(map) {
var bounds = map.getBounds(),
southWest = bounds.getSouthWest(),
northEast = bounds.getNorthEast(),
lngSpan = northEast.lng - southWest.lng,
latSpan = northEast.lat - southWest.lat;
return new L.LatLng(
southWest.lat + latSpan * Math.random(),
southWest.lng + lngSpan * Math.random());
}
markers.on('clusterclick', function (a) {
alert('cluster ' + a.layer.getAllChildMarkers().length);
});
markers.on('click', function (a) {
alert('marker ' + a.layer);
});
populate();
map.addLayer(markers);
L.DomUtil.get('moveone').onclick = function () {
var m = markersList[Math.floor(Math.random() * markersList.length)];
var bounds = map.getBounds(),
southWest = bounds.getSouthWest(),
northEast = bounds.getNorthEast(),
lngSpan = northEast.lng - southWest.lng,
latSpan = northEast.lat - southWest.lat;
m.setLatLng(new L.LatLng(
southWest.lat + latSpan * 0.5,
southWest.lng + lngSpan * 0.5));
};
</script>
</body>
</html>
@@ -3,8 +3,8 @@
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7/leaflet.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ==" crossorigin="" />
<script src="https://unpkg.com/[email protected]/dist/leaflet-src.js" integrity="sha512-WXoSHqw/t26DszhdMhOXOkI7qCiv5QWXhH9R7CgvgZMHz1ImlkVQ3uNsiQKu5wwbbxtPzFXd1hK4tzno2VqhpA==" crossorigin=""></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="screen.css" />
@@ -20,9 +20,9 @@
<span>Mouse over a cluster to see the bounds of its children and click a cluster to zoom to those bounds</span>
<script type="text/javascript">
var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
var tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}),
latlng = L.latLng(50.5, 30.51);
@@ -3,8 +3,8 @@
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7/leaflet.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ==" crossorigin="" />
<script src="https://unpkg.com/[email protected]/dist/leaflet-src.js" integrity="sha512-WXoSHqw/t26DszhdMhOXOkI7qCiv5QWXhH9R7CgvgZMHz1ImlkVQ3uNsiQKu5wwbbxtPzFXd1hK4tzno2VqhpA==" crossorigin=""></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="screen.css" />
@@ -20,9 +20,9 @@
<span>Mouse over a cluster to see the bounds of its children and click a cluster to zoom to those bounds</span>
<script type="text/javascript">
var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
var tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}),
latlng = L.latLng(0.78, 102.37);
@@ -39,14 +39,14 @@
var popupText = 'geometry type: ' + feature.geometry.type;
if (feature.properties.color) {
popupText += '<br/>color: ' + feature.properties.color
popupText += '<br/>color: ' + feature.properties.color;
}
layer.bindPopup(popupText);
}
});
geojson.addLayer(new L.Marker(new L.LatLng(2.745530718801952, 105.194091796875)))
geojson.addLayer(new L.Marker(new L.LatLng(2.745530718801952, 105.194091796875)));
var eye1 = new L.Marker(new L.LatLng(-0.7250783020332547, 101.8212890625));
var eye2 = new L.Marker(new L.LatLng(-0.7360637370492077, 103.2275390625));
@@ -0,0 +1,104 @@
<!DOCTYPE html>
<html>
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ==" crossorigin="" />
<script src="https://unpkg.com/[email protected]/dist/leaflet-src.js" integrity="sha512-WXoSHqw/t26DszhdMhOXOkI7qCiv5QWXhH9R7CgvgZMHz1ImlkVQ3uNsiQKu5wwbbxtPzFXd1hK4tzno2VqhpA==" crossorigin=""></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="screen.css" />
<link rel="stylesheet" href="../dist/MarkerCluster.css" />
<script src="../dist/leaflet.markercluster-src.js"></script>
<style>
.myclusterA {
width: 40px;
height: 40px;
background-color: greenyellow;
text-align: center;
font-size: 24px;
}
.myclusterB {
width: 40px;
height: 40px;
background-color: red;
text-align: center;
font-size: 24px;
}
</style>
</head>
<body>
<div id="map"></div>
<script type="text/javascript">
var tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}),
latlng = L.latLng(50.5, 30.51);
var map = L.map('map', {center: latlng, zoom: 15, layers: [tiles]});
//Define a pane above the default markerPane
map.createPane('paneB').style.zIndex = 610;
//Create two markerClusterGroups, one in the default markerPane, one in our custom pane
var markersA = createMarkerClusterGroup('myclusterA');
var markersB = createMarkerClusterGroup('myclusterB', 'paneB');
function createMarkerClusterGroup(className, pane) {
var options = {
maxClusterRadius: 120,
iconCreateFunction: function (cluster) {
var markers = cluster.getAllChildMarkers();
var n = 0;
for (var i = 0; i < markers.length; i++) {
n += markers[i].number;
}
return L.divIcon({ html: n, className: className, iconSize: L.point(40, 40) });
},
//Disable all of the defaults & specify the pane:
spiderfyOnMaxZoom: false,
showCoverageOnHover: false,
zoomToBoundsOnClick: false
};
if (pane) {
options.clusterPane = pane;
}
var mcg = L.markerClusterGroup(options);
return(mcg);
}
function populate(markers) {
for (var i = 0; i < 100; i++) {
var m = L.marker(getRandomLatLng(map), { title: i });
m.number = i;
markers.addLayer(m);
}
return false;
}
function getRandomLatLng(map) {
var bounds = map.getBounds(),
southWest = bounds.getSouthWest(),
northEast = bounds.getNorthEast(),
lngSpan = northEast.lng - southWest.lng,
latSpan = northEast.lat - southWest.lat;
return L.latLng(
southWest.lat + latSpan * Math.random(),
southWest.lng + lngSpan * Math.random());
}
populate(markersA);
populate(markersB);
map.addLayer(markersA);
map.addLayer(markersB);
</script>
</body>
</html>
@@ -3,8 +3,8 @@
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7/leaflet.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ==" crossorigin="" />
<script src="https://unpkg.com/[email protected]/dist/leaflet-src.js" integrity="sha512-WXoSHqw/t26DszhdMhOXOkI7qCiv5QWXhH9R7CgvgZMHz1ImlkVQ3uNsiQKu5wwbbxtPzFXd1hK4tzno2VqhpA==" crossorigin=""></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="screen.css" />
@@ -20,9 +20,9 @@
<span>Markers will show on the bottom 2 zoom levels even though the markers would normally cluster.</span>
<script type="text/javascript">
var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
var tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors, Points &copy 2012 LINZ'
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Points &copy 2012 LINZ'
}),
latlng = L.latLng(-37.82, 175.24);
@@ -3,8 +3,8 @@
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7/leaflet.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ==" crossorigin="" />
<script src="https://unpkg.com/[email protected]/dist/leaflet-src.js" integrity="sha512-WXoSHqw/t26DszhdMhOXOkI7qCiv5QWXhH9R7CgvgZMHz1ImlkVQ3uNsiQKu5wwbbxtPzFXd1hK4tzno2VqhpA==" crossorigin=""></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<link rel="stylesheet" href="mobile.css" />
@@ -19,9 +19,9 @@
<script type="text/javascript">
var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
var tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors, Points &copy 2012 LINZ'
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Points &copy 2012 LINZ'
}),
latlng = L.latLng(-37.821, 175.22);
@@ -3,8 +3,8 @@
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7/leaflet.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ==" crossorigin="" />
<script src="https://unpkg.com/[email protected]/dist/leaflet-src.js" integrity="sha512-WXoSHqw/t26DszhdMhOXOkI7qCiv5QWXhH9R7CgvgZMHz1ImlkVQ3uNsiQKu5wwbbxtPzFXd1hK4tzno2VqhpA==" crossorigin=""></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="screen.css" />
@@ -21,9 +21,9 @@
<span>Mouse over a cluster to see the bounds of its children and click a cluster to zoom to those bounds</span>
<script type="text/javascript">
var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
var tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors, Points &copy 2012 LINZ'
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Points &copy 2012 LINZ'
}),
latlng = L.latLng(-37.89, 175.46);
@@ -3,8 +3,8 @@
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7/leaflet.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ==" crossorigin="" />
<script src="https://unpkg.com/[email protected]/dist/leaflet-src.js" integrity="sha512-WXoSHqw/t26DszhdMhOXOkI7qCiv5QWXhH9R7CgvgZMHz1ImlkVQ3uNsiQKu5wwbbxtPzFXd1hK4tzno2VqhpA==" crossorigin=""></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="screen.css" />
@@ -20,9 +20,9 @@
<span>Mouse over a cluster to see the bounds of its children and click a cluster to zoom to those bounds</span>
<script type="text/javascript">
var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
var tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors, Points &copy 2012 LINZ'
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Points &copy 2012 LINZ'
}),
latlng = L.latLng(-37.82, 175.24);
@@ -3,8 +3,8 @@
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7/leaflet.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ==" crossorigin="" />
<script src="https://unpkg.com/[email protected]/dist/leaflet-src.js" integrity="sha512-WXoSHqw/t26DszhdMhOXOkI7qCiv5QWXhH9R7CgvgZMHz1ImlkVQ3uNsiQKu5wwbbxtPzFXd1hK4tzno2VqhpA==" crossorigin=""></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="screen.css" />
@@ -22,9 +22,9 @@
<div id="map"></div>
<span>Mouse over a cluster to see the bounds of its children and click a cluster to zoom to those bounds</span>
<script type="text/javascript">
var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
var tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors, Points &copy 2012 LINZ'
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Points &copy 2012 LINZ'
}),
latlng = L.latLng(-37.79, 175.27);
@@ -60,7 +60,7 @@
markerList.push(marker);
}
for (var i = 0; i < addressPoints2.length; i++) {
var a = addressPoints[i];
var a = addressPoints2[i];
var title = a[2];
var marker = L.marker(L.latLng(a[0], a[1]), { title: title });
marker.bindPopup(title);
@@ -3,8 +3,8 @@
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7/leaflet.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ==" crossorigin="" />
<script src="https://unpkg.com/[email protected]/dist/leaflet-src.js" integrity="sha512-WXoSHqw/t26DszhdMhOXOkI7qCiv5QWXhH9R7CgvgZMHz1ImlkVQ3uNsiQKu5wwbbxtPzFXd1hK4tzno2VqhpA==" crossorigin=""></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="screen.css" />
@@ -19,9 +19,9 @@
<script type="text/javascript">
var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
var tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}),
latlng = L.latLng(50.5, 30.51);
@@ -3,8 +3,8 @@
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7/leaflet.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ==" crossorigin="" />
<script src="https://unpkg.com/[email protected]/dist/leaflet-src.js" integrity="sha512-WXoSHqw/t26DszhdMhOXOkI7qCiv5QWXhH9R7CgvgZMHz1ImlkVQ3uNsiQKu5wwbbxtPzFXd1hK4tzno2VqhpA==" crossorigin=""></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="screen.css" />
@@ -20,9 +20,9 @@
<script type="text/javascript">
var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
var tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}),
latlng = L.latLng(50.5, 30.51);
@@ -3,8 +3,8 @@
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7/leaflet.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ==" crossorigin="" />
<script src="https://unpkg.com/[email protected]/dist/leaflet-src.js" integrity="sha512-WXoSHqw/t26DszhdMhOXOkI7qCiv5QWXhH9R7CgvgZMHz1ImlkVQ3uNsiQKu5wwbbxtPzFXd1hK4tzno2VqhpA==" crossorigin=""></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="screen.css" />
@@ -19,9 +19,9 @@
<script type="text/javascript">
var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
var tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}),
latlng = L.latLng(50.5, 30.51);
@@ -3,8 +3,8 @@
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7/leaflet.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ==" crossorigin="" />
<script src="https://unpkg.com/[email protected]/dist/leaflet-src.js" integrity="sha512-WXoSHqw/t26DszhdMhOXOkI7qCiv5QWXhH9R7CgvgZMHz1ImlkVQ3uNsiQKu5wwbbxtPzFXd1hK4tzno2VqhpA==" crossorigin=""></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="screen.css" />
@@ -21,9 +21,9 @@
<span>When clicked we will zoom down to a marker, spiderfying if required to show it and then open its popup</span>
<script type="text/javascript">
var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
var tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}),
latlng = L.latLng(-37.82, 175.24);
@@ -3,8 +3,8 @@
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7/leaflet.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ==" crossorigin="" />
<script src="https://unpkg.com/[email protected]/dist/leaflet-src.js" integrity="sha512-WXoSHqw/t26DszhdMhOXOkI7qCiv5QWXhH9R7CgvgZMHz1ImlkVQ3uNsiQKu5wwbbxtPzFXd1hK4tzno2VqhpA==" crossorigin=""></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="screen.css" />
@@ -20,9 +20,9 @@
<script type="text/javascript">
var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
var tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}),
latlng = new L.LatLng(50.5, 30.51);
@@ -3,8 +3,8 @@
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7/leaflet-src.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ==" crossorigin="" />
<script src="https://unpkg.com/leaflet@1.0.3/dist/leaflet-src.js" integrity="sha512-WXoSHqw/t26DszhdMhOXOkI7qCiv5QWXhH9R7CgvgZMHz1ImlkVQ3uNsiQKu5wwbbxtPzFXd1hK4tzno2VqhpA==" crossorigin=""></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="../screen.css" />
@@ -25,9 +25,9 @@
<span id="time"></span>
<script type="text/javascript">
var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
var tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}),
latlng = new L.LatLng(50.5, 30.51);
@@ -3,8 +3,8 @@
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7/leaflet-src.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ==" crossorigin="" />
<script src="https://unpkg.com/leaflet@1.0.3/dist/leaflet-src.js" integrity="sha512-WXoSHqw/t26DszhdMhOXOkI7qCiv5QWXhH9R7CgvgZMHz1ImlkVQ3uNsiQKu5wwbbxtPzFXd1hK4tzno2VqhpA==" crossorigin=""></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="../screen.css" />
@@ -23,9 +23,9 @@
<span>Bug <a href="https://github.com/danzel/Leaflet.markercluster/issues/69">#69</a>. Click the button 2+ times. Zoom out. Should just be a single cluster but instead one of the child markers is still visible.</span><br/>
<script type="text/javascript">
var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
var tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}),
latlng = new L.LatLng(50.5, 30.51);
@@ -3,8 +3,8 @@
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7/leaflet-src.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ==" crossorigin="" />
<script src="https://unpkg.com/leaflet@1.0.3/dist/leaflet-src.js" integrity="sha512-WXoSHqw/t26DszhdMhOXOkI7qCiv5QWXhH9R7CgvgZMHz1ImlkVQ3uNsiQKu5wwbbxtPzFXd1hK4tzno2VqhpA==" crossorigin=""></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="../screen.css" />
@@ -23,9 +23,9 @@
<span id="time"></span>
<script type="text/javascript">
var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
var tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}),
latlng = new L.LatLng(50.5, 30.51);
@@ -3,8 +3,8 @@
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7/leaflet-src.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ==" crossorigin="" />
<script src="https://unpkg.com/leaflet@1.0.3/dist/leaflet-src.js" integrity="sha512-WXoSHqw/t26DszhdMhOXOkI7qCiv5QWXhH9R7CgvgZMHz1ImlkVQ3uNsiQKu5wwbbxtPzFXd1hK4tzno2VqhpA==" crossorigin=""></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="../screen.css" />
@@ -24,9 +24,9 @@
<span id="time"></span>
<script type="text/javascript">
var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
var tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}),
latlng = new L.LatLng(-37.36142550190516, 174.254150390625);
@@ -3,8 +3,8 @@
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7/leaflet-src.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ==" crossorigin="" />
<script src="https://unpkg.com/leaflet@1.0.3/dist/leaflet-src.js" integrity="sha512-WXoSHqw/t26DszhdMhOXOkI7qCiv5QWXhH9R7CgvgZMHz1ImlkVQ3uNsiQKu5wwbbxtPzFXd1hK4tzno2VqhpA==" crossorigin=""></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="../screen.css" />
@@ -38,9 +38,9 @@
return new L.DivIcon({ html: '<div><span>' + childCount + '</span></div>', className: 'marker-cluster' + c, iconSize: new L.Point(270, 270) });
};
var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
var tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}),
latlng = new L.LatLng(54.18815548107151, 6.1083984375);
@@ -3,8 +3,8 @@
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7/leaflet.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ==" crossorigin="" />
<script src="https://unpkg.com/[email protected]/dist/leaflet-src.js" integrity="sha512-WXoSHqw/t26DszhdMhOXOkI7qCiv5QWXhH9R7CgvgZMHz1ImlkVQ3uNsiQKu5wwbbxtPzFXd1hK4tzno2VqhpA==" crossorigin=""></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="../screen.css" />
@@ -51,9 +51,9 @@
var trainPosition = 0,
trainDirection = 'up';
var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
var tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}),
map = new L.Map('map', {zoom: 15, layers: [tiles]}),
markers = new L.MarkerClusterGroup({ spiderfyOnMaxZoom: false, showCoverageOnHover: false, zoomToBoundsOnClick: false });
@@ -3,8 +3,8 @@
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7/leaflet-src.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ==" crossorigin="" />
<script src="https://unpkg.com/leaflet@1.0.3/dist/leaflet-src.js" integrity="sha512-WXoSHqw/t26DszhdMhOXOkI7qCiv5QWXhH9R7CgvgZMHz1ImlkVQ3uNsiQKu5wwbbxtPzFXd1hK4tzno2VqhpA==" crossorigin=""></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="../screen.css" />
@@ -24,9 +24,9 @@
<script type="text/javascript">
var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
var tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}),
latlng = new L.LatLng(40.782982, -73.969452);
@@ -3,8 +3,8 @@
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7/leaflet-src.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ==" crossorigin="" />
<script src="https://unpkg.com/leaflet@1.0.3/dist/leaflet-src.js" integrity="sha512-WXoSHqw/t26DszhdMhOXOkI7qCiv5QWXhH9R7CgvgZMHz1ImlkVQ3uNsiQKu5wwbbxtPzFXd1hK4tzno2VqhpA==" crossorigin=""></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="../screen.css" />
@@ -26,9 +26,9 @@
<script type="text/javascript">
var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
var tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}),
latlng = new L.LatLng(50.5, 30.51);
@@ -3,8 +3,8 @@
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7/leaflet-src.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ==" crossorigin="" />
<script src="https://unpkg.com/leaflet@1.0.3/dist/leaflet-src.js" integrity="sha512-WXoSHqw/t26DszhdMhOXOkI7qCiv5QWXhH9R7CgvgZMHz1ImlkVQ3uNsiQKu5wwbbxtPzFXd1hK4tzno2VqhpA==" crossorigin=""></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="../screen.css" />
@@ -26,9 +26,9 @@
<script type="text/javascript">
var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
var tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}),
latlng = new L.LatLng(50.5, 30.51);
@@ -3,8 +3,8 @@
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7/leaflet-src.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ==" crossorigin="" />
<script src="https://unpkg.com/leaflet@1.0.3/dist/leaflet-src.js" integrity="sha512-WXoSHqw/t26DszhdMhOXOkI7qCiv5QWXhH9R7CgvgZMHz1ImlkVQ3uNsiQKu5wwbbxtPzFXd1hK4tzno2VqhpA==" crossorigin=""></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="../screen.css" />
@@ -28,9 +28,9 @@
var map = new L.Map('map');
var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
var tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
});
map.addLayer(tiles);
@@ -3,8 +3,8 @@
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7/leaflet-src.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ==" crossorigin="" />
<script src="https://unpkg.com/leaflet@1.0.3/dist/leaflet-src.js" integrity="sha512-WXoSHqw/t26DszhdMhOXOkI7qCiv5QWXhH9R7CgvgZMHz1ImlkVQ3uNsiQKu5wwbbxtPzFXd1hK4tzno2VqhpA==" crossorigin=""></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="../screen.css" />
@@ -27,9 +27,9 @@
//Mobile does different bounds to desktop, makes the bug easier to reproduce
L.Browser.mobile = true;
var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
var tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}),
latlng = new L.LatLng(50.5, 30.51);
@@ -3,8 +3,8 @@
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7/leaflet-src.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ==" crossorigin="" />
<script src="https://unpkg.com/leaflet@1.0.3/dist/leaflet-src.js" integrity="sha512-WXoSHqw/t26DszhdMhOXOkI7qCiv5QWXhH9R7CgvgZMHz1ImlkVQ3uNsiQKu5wwbbxtPzFXd1hK4tzno2VqhpA==" crossorigin=""></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="../screen.css" />
@@ -24,9 +24,9 @@
<span id="time"></span>
<script type="text/javascript">
var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
var tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}),
latlng = new L.LatLng(50.5, 30.51);
@@ -3,8 +3,8 @@
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7/leaflet-src.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ==" crossorigin="" />
<script src="https://unpkg.com/leaflet@1.0.3/dist/leaflet-src.js" integrity="sha512-WXoSHqw/t26DszhdMhOXOkI7qCiv5QWXhH9R7CgvgZMHz1ImlkVQ3uNsiQKu5wwbbxtPzFXd1hK4tzno2VqhpA==" crossorigin=""></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="../screen.css" />
@@ -24,9 +24,9 @@
<span id="time"></span>
<script type="text/javascript">
var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
var tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}),
latlng = new L.LatLng(50.5, 30.51);
@@ -0,0 +1,83 @@
<!DOCTYPE html>
<html>
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" integrity="sha512-07I2e+7D8p6he1SIM+1twR5TIrhUQn9+I6yjqD53JQjFiMf8EtC93ty0/5vJTZGF8aAocvHYNEDJajGdNx1IsQ==" crossorigin="" />
<script src="https://unpkg.com/[email protected]/dist/leaflet-src.js" integrity="sha512-WXoSHqw/t26DszhdMhOXOkI7qCiv5QWXhH9R7CgvgZMHz1ImlkVQ3uNsiQKu5wwbbxtPzFXd1hK4tzno2VqhpA==" crossorigin=""></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="screen.css" />
<link rel="stylesheet" href="../dist/MarkerCluster.css" />
<link rel="stylesheet" href="../dist/MarkerCluster.Default.css" />
<script src="../dist/leaflet.markercluster-src.js"></script>
</head>
<body>
<div id="map"></div>
<button id="doit">Remove marker #1</button><button id="doit2">Remove marker #2</button><br/>
<span>New Bug. Spiderfy the cluster then click the button #1. All markers disapear, but it should remain marker #2.</span><br/>
<span>New Bug. Spiderfy the cluster then click the button #2. All markers disapear, but it should remain marker #1.</span><br/>
<script type="text/javascript">
var tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}),
latlng = new L.LatLng(50.5, 30.51);
var map = new L.Map('map', {center: latlng, zoom: 15, layers: [tiles]});
var markers = new L.MarkerClusterGroup({ disableClusteringAtZoom: 19});
var markersList = [];
var m;
geoJsonFeature = { "type": "FeatureCollection",
"features": [ { "type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [30.51, 50.5]
}
}
]
};
m = L.geoJson(geoJsonFeature, {
pointToLayer: function (feature, latlng) {
return L.marker(latlng);
}
});
markersList.push(m);
markers.addLayer(m);
geoJsonFeature = { "type": "FeatureCollection",
"features": [ { "type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [30.5101, 50.5]
}
}
]
};
m = L.geoJson(geoJsonFeature, {
pointToLayer: function (feature, latlng) {
return L.marker(latlng);
}
});
markersList.push(m);
markers.addLayer(m);
map.addLayer(markers);
L.DomUtil.get('doit').onclick = function () {
markers.removeLayer(markersList[0]);
};
L.DomUtil.get('doit2').onclick = function () {
markers.removeLayer(markersList[1]);
};
</script>
</body>
</html>
+6 -6
View File
@@ -1,22 +1,22 @@
{
"name": "leaflet.markercluster",
"repository": "https://github.com/Leaflet/Leaflet.markercluster",
"version": "0.4.0",
"version": "1.2.0",
"description": "Provides Beautiful Animated Marker Clustering functionality for Leaflet",
"dependencies": {
"leaflet": "~0.7.1"
},
"devDependencies": {
"jshint": "~2.1.3",
"mocha": "~1.10.0",
"karma": "~0.8.5",
"uglify-js": "~2.3.6",
"jake": "~0.5.16"
"jake": "~0.5.16",
"magic-string": "^0.7.0",
"leaflet": "~1.0.3"
},
"main": "dist/leaflet.markercluster.js",
"scripts": {
"test": "jake test",
"prepublish": "jake"
},
"keywords": ["gis", "map"]
"keywords": ["gis", "map"],
"license": "MIT"
}
+13
View File
@@ -20,6 +20,7 @@
<script type="text/javascript" src="../src/MarkerCluster.QuickHull.js"></script>
<script type="text/javascript" src="../src/MarkerCluster.Spiderfier.js"></script>
<script type="text/javascript" src="../src/MarkerOpacity.js"></script>
<script type="text/javascript" src="../src/MarkerClusterGroup.Refresh.js"></script>
<script>
mocha.setup('bdd');
@@ -32,11 +33,17 @@
<script type="text/javascript" src="suites/LeafletSpec.js"></script>
<script type="text/javascript" src="suites/DistanceGridSpec.js"></script>
<script type="text/javascript" src="suites/QuickHullSpec.js"></script>
<script type="text/javascript" src="suites/AddLayer.MultipleSpec.js"></script>
<script type="text/javascript" src="suites/AddLayer.SingleSpec.js"></script>
<script type="text/javascript" src="suites/AddLayersSpec.js"></script>
<script type="text/javascript" src="suites/animateOptionSpec.js"></script>
<script type="text/javascript" src="suites/singleMarkerModeSpec.js"></script>
<script type="text/javascript" src="suites/ChildChangingIconSupportSpec.js"></script>
<script type="text/javascript" src="suites/markerMoveSupportSpec.js"></script>
<script type="text/javascript" src="suites/CircleMarkerSupportSpec.js"></script>
<script type="text/javascript" src="suites/CircleSupportSpec.js"></script>
@@ -55,9 +62,15 @@
<script type="text/javascript" src="suites/RemoveLayerSpec.js"></script>
<script type="text/javascript" src="suites/removeLayersSpec.js"></script>
<script type="text/javascript" src="suites/spiderfySpec.js"></script>
<script type="text/javascript" src="suites/unspiderfySpec.js"></script>
<script type="text/javascript" src="suites/zoomAnimationSpec.js"></script>
<script type="text/javascript" src="suites/RememberOpacity.js"></script>
<script type="text/javascript" src="suites/supportNegativeZoomSpec.js"></script>
<script type="text/javascript" src="suites/RefreshSpec.js"></script>
<script type="text/javascript" src="suites/removeOutsideVisibleBoundsSpec.js"></script>
<script type="text/javascript" src="suites/nonIntegerZoomSpec.js"></script>
<script>
(window.mochaPhantomJS || window.mocha).run();
+1 -1
View File
@@ -1,6 +1,6 @@
// Karma configuration
var libSources = require(__dirname+'/../build/build.js').getFiles();
var leafletSources = require(__dirname+'/../node_modules/leaflet/build/build.js').getFiles();
var leafletSources = require(__dirname+'/../node_modules/leaflet/build/build.js').getFiles(); // Caution Leaflet 1.0.0-beta.2 build needs magic-string
// base path, that will be used to resolve files and exclude
basePath = '';
@@ -1,26 +1,70 @@
describe('addLayer adding a single marker', function () {
var map, div;
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Nothing for this test suite.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
});
afterEach(function () {
document.body.removeChild(div);
if (group instanceof L.MarkerClusterGroup) {
group.clearLayers();
map.removeLayer(group);
}
// Throw away group as it can be assigned with different configurations between tests.
group = null;
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var div, map, group;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
/////////////////////////////
// TESTS
/////////////////////////////
it('appears when added to the group before the group is added to the map', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
group.addLayer(marker);
@@ -29,9 +73,11 @@
expect(marker._icon).to.not.be(undefined);
expect(marker._icon.parentNode).to.be(map._panes.markerPane);
});
it('appears when added to the group after the group is added to the map', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
map.addLayer(group);
@@ -40,9 +86,11 @@
expect(marker._icon).to.not.be(undefined);
expect(marker._icon.parentNode).to.be(map._panes.markerPane);
});
it('appears (using animations) when added after the group is added to the map', function () {
var group = new L.MarkerClusterGroup({ animateAddingMarkers: true });
group = new L.MarkerClusterGroup({ animateAddingMarkers: true });
var marker = new L.Marker([1.5, 1.5]);
map.addLayer(group);
@@ -52,10 +100,10 @@
expect(marker._icon.parentNode).to.be(map._panes.markerPane);
});
it('does not appear when too far away when added before the group is added to the map', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var marker = new L.Marker([3.5, 1.5]);
group.addLayer(marker);
@@ -63,9 +111,11 @@
expect(marker._icon).to.be(undefined);
});
it('does not appear when too far away when added after the group is added to the map', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var marker = new L.Marker([3.5, 1.5]);
map.addLayer(group);
@@ -74,5 +124,31 @@
expect(marker._icon).to.be(undefined);
});
it('passes control to addLayers when marker is a Layer Group', function () {
group = new L.MarkerClusterGroup();
var marker1 = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
var layerGroup = new L.LayerGroup([marker1, marker2]);
map.addLayer(group);
group.addLayer(layerGroup);
expect(group._topClusterLevel.getChildCount()).to.equal(2);
expect(marker1._icon).to.be(undefined);
expect(marker2._icon).to.be(undefined);
expect(map._panes.markerPane.childNodes.length).to.be(1);
});
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});
@@ -1,25 +1,70 @@
describe('addLayers adding multiple markers', function () {
var map, div;
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Nothing for this test suite.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
});
afterEach(function () {
document.body.removeChild(div);
if (group instanceof L.MarkerClusterGroup) {
group.clearLayers();
map.removeLayer(group);
}
// Throw away group as it can be assigned with different configurations between tests.
group = null;
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var div, map, group;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
/////////////////////////////
// TESTS
/////////////////////////////
it('creates a cluster when 2 overlapping markers are added before the group is added to the map', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
@@ -34,7 +79,8 @@
it('creates a cluster when 2 overlapping markers are added after the group is added to the map', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
@@ -47,11 +93,10 @@
expect(map._panes.markerPane.childNodes.length).to.be(1);
});
it('creates a cluster and marker when 2 overlapping markers and one non-overlapping are added before the group is added to the map', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
var marker3 = new L.Marker([3.0, 1.5]);
@@ -65,9 +110,11 @@
expect(map._panes.markerPane.childNodes.length).to.be(2);
});
it('creates a cluster and marker when 2 overlapping markers and one non-overlapping are added after the group is added to the map', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
var marker3 = new L.Marker([3.0, 1.5]);
@@ -82,4 +129,31 @@
expect(map._panes.markerPane.childNodes.length).to.be(2);
});
});
it('handles nested Layer Groups', function () {
group = new L.MarkerClusterGroup();
var marker1 = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
var marker3 = new L.Marker([3.0, 1.5]);
var layerGroup = new L.LayerGroup([marker1, new L.LayerGroup([marker2])]);
map.addLayer(group);
group.addLayers([layerGroup, marker3]);
expect(marker1._icon).to.be(undefined);
expect(marker2._icon).to.be(undefined);
expect(marker3._icon.parentNode).to.be(map._panes.markerPane);
expect(map._panes.markerPane.childNodes.length).to.be(2);
});
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});
@@ -1,54 +1,106 @@
describe('support for CircleMarker elements', function () {
var map, div, clock;
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
clock = sinon.useFakeTimers();
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
});
afterEach(function () {
if (group instanceof L.MarkerClusterGroup) {
group.removeLayers(group.getLayers());
map.removeLayer(group);
}
// group must be thrown away since we are testing it with a potentially
// different configuration at each test.
group = null;
clock.restore();
document.body.removeChild(div);
clock = null;
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var div, map, group, clock;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
/////////////////////////////
// TESTS
/////////////////////////////
it('appears when added to the group before the group is added to the map', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var marker = new L.CircleMarker([1.5, 1.5]);
group.addLayer(marker);
map.addLayer(group);
expect(marker._container).to.not.be(undefined);
expect(marker._container.parentNode).to.be(map._pathRoot);
// Leaflet 1.0.0 now uses an intermediate L.Renderer.
// marker > _path > _rootGroup (g) > _container (svg) > pane (div)
expect(marker._path.parentNode.parentNode).to.not.be(undefined);
expect(marker._path.parentNode.parentNode.parentNode).to.be(map.getPane('overlayPane'));
clock.tick(1000);
});
it('appears when added to the group after the group is added to the map', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var marker = new L.CircleMarker([1.5, 1.5]);
group.addLayer(marker);
map.addLayer(group);
expect(marker._container).to.not.be(undefined);
expect(marker._container.parentNode).to.be(map._pathRoot);
expect(marker._path.parentNode.parentNode).to.not.be(undefined);
expect(marker._path.parentNode.parentNode.parentNode).to.be(map.getPane('overlayPane'));
clock.tick(1000);
});
it('appears animated when added to the group after the group is added to the map', function () {
var group = new L.MarkerClusterGroup({ animateAddingMarkers: true });
group = new L.MarkerClusterGroup({ animateAddingMarkers: true });
var marker = new L.CircleMarker([1.5, 1.5]);
var marker2 = new L.CircleMarker([1.5, 1.5]);
@@ -56,35 +108,37 @@
group.addLayer(marker);
group.addLayer(marker2);
expect(marker._container.parentNode).to.be(map._pathRoot);
expect(marker2._container.parentNode).to.be(map._pathRoot);
expect(marker._path.parentNode.parentNode.parentNode).to.be(map.getPane('overlayPane'));
expect(marker2._path.parentNode.parentNode.parentNode).to.be(map.getPane('overlayPane'));
clock.tick(1000);
expect(marker._container.parentNode).to.be(null);
expect(marker2._container.parentNode).to.be(null);
expect(marker._path.parentNode).to.be(null);
expect(marker2._path.parentNode).to.be(null);
});
it('creates a cluster when 2 overlapping markers are added before the group is added to the map', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var marker = new L.CircleMarker([1.5, 1.5]);
var marker2 = new L.CircleMarker([1.5, 1.5]);
group.addLayers([marker, marker2]);
map.addLayer(group);
expect(marker._container).to.be(undefined);
expect(marker2._container).to.be(undefined);
expect(marker._path).to.be(undefined);
expect(marker2._path).to.be(undefined);
expect(map._panes.markerPane.childNodes.length).to.be(1);
clock.tick(1000);
});
it('creates a cluster when 2 overlapping markers are added after the group is added to the map', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var marker = new L.CircleMarker([1.5, 1.5]);
var marker2 = new L.CircleMarker([1.5, 1.5]);
@@ -92,8 +146,8 @@
group.addLayer(marker);
group.addLayer(marker2);
expect(marker._container.parentNode).to.be(null); //Removed then re-added, so null
expect(marker2._container).to.be(undefined);
expect(marker._path.parentNode).to.be(null); //Removed then re-added, so null
expect(marker2._path).to.be(undefined);
expect(map._panes.markerPane.childNodes.length).to.be(1);
@@ -102,20 +156,29 @@
it('disappears when removed from the group', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var marker = new L.CircleMarker([1.5, 1.5]);
group.addLayer(marker);
map.addLayer(group);
expect(marker._container).to.not.be(undefined);
expect(marker._container.parentNode).to.be(map._pathRoot);
expect(marker._path.parentNode).to.not.be(undefined);
expect(marker._path.parentNode.parentNode.parentNode).to.be(map.getPane('overlayPane'));
group.removeLayer(marker);
expect(marker._container.parentNode).to.be(null);
expect(marker._path.parentNode).to.be(null);
clock.tick(1000);
});
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});
@@ -1,54 +1,106 @@
describe('support for Circle elements', function () {
var map, div, clock;
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
clock = sinon.useFakeTimers();
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
});
afterEach(function () {
if (group instanceof L.MarkerClusterGroup) {
group.removeLayers(group.getLayers());
map.removeLayer(group);
}
// group must be thrown away since we are testing it with a potentially
// different configuration at each test.
group = null;
clock.restore();
document.body.removeChild(div);
clock = null;
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var div, map, group, clock;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
/////////////////////////////
// TESTS
/////////////////////////////
it('appears when added to the group before the group is added to the map', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var marker = new L.Circle([1.5, 1.5], 200);
group.addLayer(marker);
map.addLayer(group);
expect(marker._container).to.not.be(undefined);
expect(marker._container.parentNode).to.be(map._pathRoot);
// Leaflet 1.0.0 now uses an intermediate L.Renderer.
// marker > _path > _rootGroup (g) > _container (svg) > pane (div)
expect(marker._path.parentNode).to.not.be(undefined);
expect(marker._path.parentNode.parentNode.parentNode).to.be(map.getPane('overlayPane'));
clock.tick(1000);
});
it('appears when added to the group after the group is added to the map', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var marker = new L.Circle([1.5, 1.5], 200);
group.addLayer(marker);
map.addLayer(group);
expect(marker._container).to.not.be(undefined);
expect(marker._container.parentNode).to.be(map._pathRoot);
expect(marker._path.parentNode).to.not.be(undefined);
expect(marker._path.parentNode.parentNode.parentNode).to.be(map.getPane('overlayPane'));
clock.tick(1000);
});
it('appears animated when added to the group after the group is added to the map', function () {
var group = new L.MarkerClusterGroup({ animateAddingMarkers: true });
group = new L.MarkerClusterGroup({ animateAddingMarkers: true });
var marker = new L.Circle([1.5, 1.5], 200);
var marker2 = new L.Circle([1.5, 1.5], 200);
@@ -56,32 +108,34 @@
group.addLayer(marker);
group.addLayer(marker2);
expect(marker._container.parentNode).to.be(map._pathRoot);
expect(marker2._container.parentNode).to.be(map._pathRoot);
expect(marker._path.parentNode.parentNode.parentNode).to.be(map.getPane('overlayPane'));
expect(marker2._path.parentNode.parentNode.parentNode).to.be(map.getPane('overlayPane'));
clock.tick(1000);
});
it('creates a cluster when 2 overlapping markers are added before the group is added to the map', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var marker = new L.Circle([1.5, 1.5], 200);
var marker2 = new L.Circle([1.5, 1.5], 200);
group.addLayers([marker, marker2]);
map.addLayer(group);
expect(marker._container).to.be(undefined);
expect(marker2._container).to.be(undefined);
expect(marker._path).to.be(undefined);
expect(marker2._path).to.be(undefined);
expect(map._panes.markerPane.childNodes.length).to.be(1);
clock.tick(1000);
});
it('creates a cluster when 2 overlapping markers are added after the group is added to the map', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var marker = new L.Circle([1.5, 1.5], 200);
var marker2 = new L.Circle([1.5, 1.5], 200);
@@ -89,8 +143,8 @@
group.addLayer(marker);
group.addLayer(marker2);
expect(marker._container.parentNode).to.be(null); //Removed then re-added, so null
expect(marker2._container).to.be(undefined);
expect(marker._path.parentNode).to.be(null); //Removed then re-added, so null
expect(marker2._path).to.be(undefined);
expect(map._panes.markerPane.childNodes.length).to.be(1);
@@ -99,20 +153,29 @@
it('disappears when removed from the group', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var marker = new L.Circle([1.5, 1.5], 200);
group.addLayer(marker);
map.addLayer(group);
expect(marker._container).to.not.be(undefined);
expect(marker._container.parentNode).to.be(map._pathRoot);
expect(marker._path.parentNode).to.not.be(undefined);
expect(marker._path.parentNode.parentNode.parentNode).to.be(map.getPane('overlayPane'));
group.removeLayer(marker);
expect(marker._container.parentNode).to.be(null);
expect(marker._path.parentNode).to.be(null);
clock.tick(1000);
});
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});
@@ -18,4 +18,24 @@
done();
});
});
it('getNearObject', function () {
var grid = new L.DistanceGrid(100),
obj = {};
grid.addObject(obj, { x: 0, y: 0 });
expect(grid.getNearObject({ x: 50, y: 50 })).to.equal(obj);
expect(grid.getNearObject({ x: 100, y: 0 })).to.equal(obj);
});
it('getNearObject with cellSize 0', function () {
var grid = new L.DistanceGrid(0),
obj = {};
grid.addObject(obj, { x: 0, y: 0 });
expect(grid.getNearObject({ x: 50, y: 50 })).to.equal(null);
expect(grid.getNearObject({ x: 0, y: 0 })).to.equal(obj);
});
});
@@ -1,63 +1,115 @@
describe('adding non point data works', function () {
var map, div;
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Nothing for this test suite.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
});
afterEach(function () {
document.body.removeChild(div);
if (group instanceof L.MarkerClusterGroup) {
group.removeLayers(group.getLayers());
map.removeLayer(group);
}
// group must be thrown away since we are testing it with a potentially
// different configuration at each test.
group = null;
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var div, map, group;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
/////////////////////////////
// TESTS
/////////////////////////////
it('Allows adding a polygon before via addLayer', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0,2.0], [1.5, 2.0]]);
group.addLayer(polygon);
map.addLayer(group);
expect(polygon._container).to.not.be(undefined);
expect(polygon._container.parentNode).to.be(map._pathRoot);
// Leaflet 1.0.0 now uses an intermediate L.Renderer.
// polygon > _path > _rootGroup (g) > _container (svg) > pane (div)
expect(polygon._path).to.not.be(undefined);
expect(polygon._path.parentNode.parentNode.parentNode).to.be(map.getPane('overlayPane'));
expect(group.hasLayer(polygon));
});
it('Allows adding a polygon before via addLayers([])', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayers([polygon]);
map.addLayer(group);
expect(polygon._container).to.not.be(undefined);
expect(polygon._container.parentNode).to.be(map._pathRoot);
expect(polygon._path).to.not.be(undefined);
expect(polygon._path.parentNode.parentNode.parentNode).to.be(map.getPane('overlayPane'));
});
it('Removes polygons from map when removed', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayer(polygon);
map.addLayer(group);
map.removeLayer(group);
expect(polygon._container.parentNode).to.be(null);
expect(polygon._path.parentNode).to.be(null);
});
describe('hasLayer', function () {
it('returns false when not added', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
expect(group.hasLayer(polygon)).to.be(false);
@@ -72,7 +124,8 @@
});
it('returns true before adding to map', function() {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayers([polygon]);
@@ -81,7 +134,8 @@
});
it('returns true after adding to map after adding polygon', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayer(polygon);
@@ -91,7 +145,8 @@
});
it('returns true after adding to map before adding polygon', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
map.addLayer(group);
@@ -99,11 +154,14 @@
expect(group.hasLayer(polygon)).to.be(true);
});
});
describe('removeLayer', function() {
it('removes before adding to map', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayer(polygon);
@@ -114,7 +172,8 @@
});
it('removes before adding to map', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayers([polygon]);
@@ -125,7 +184,8 @@
});
it('removes after adding to map after adding polygon', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayer(polygon);
@@ -137,7 +197,8 @@
});
it('removes after adding to map before adding polygon', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
map.addLayer(group);
@@ -147,11 +208,14 @@
group.removeLayer(polygon);
expect(group.hasLayer(polygon)).to.be(false);
});
});
describe('removeLayers', function () {
it('removes before adding to map', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayer(polygon);
@@ -162,7 +226,8 @@
});
it('removes before adding to map', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayers([polygon]);
@@ -173,7 +238,8 @@
});
it('removes after adding to map after adding polygon', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayer(polygon);
@@ -185,7 +251,8 @@
});
it('removes after adding to map before adding polygon', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
map.addLayer(group);
@@ -195,5 +262,15 @@
group.removeLayers([polygon]);
expect(group.hasLayer(polygon)).to.be(false);
});
});
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});
@@ -0,0 +1,97 @@
describe('Map pane selection', function() {
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
// Nothing for this test suite.
});
afterEach(function () {
if (group instanceof L.MarkerClusterGroup) {
group.clearLayers();
map.removeLayer(group);
}
// Throw away group as it can be assigned with different configurations between tests.
group = null;
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var div, map, group;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Create map pane
map.createPane('testPane');
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
/////////////////////////////
// TESTS
/////////////////////////////
it('recognizes and applies option', function() {
group = new L.MarkerClusterGroup({clusterPane: 'testPane'});
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
group.addLayers([marker, marker2]);
map.addLayer(group);
expect(map._panes.testPane.childNodes.length).to.be(1);
});
it('defaults to default marker pane', function() {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
group.addLayers([marker, marker2]);
map.addLayer(group);
expect(map._panes[L.Marker.prototype.options.pane].childNodes.length).to.be(1);
});
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});
@@ -17,19 +17,36 @@ describe('quickhull', function () {
});
describe('getConvexHull', function () {
it('creates a hull', function () {
expect(L.QuickHull.getConvexHull([
{ lat: 0, lng: 0 },
{ lat: 10, lng: 0 },
{ lat: 10, lng: 10 },
{ lat: 0, lng: 10 },
{ lat: 5, lng: 5 },
])).to.eql([
{ lat: 0, lng: 10 },
{ lat: 10, lng: 10 },
{ lat: 10, lng: 0 },
{ lat: 0, lng: 0 },
]);
});
});
it('creates a hull', function () {
expect(L.QuickHull.getConvexHull([ { lat: 0, lng: 0 },
{ lat: 10, lng: 0 },
{ lat: 10, lng: 10 },
{ lat: 0, lng: 10 },
{ lat: 5, lng: 5 }
])).to.eql([
{ lat: 0, lng: 10 },
{ lat: 10, lng: 10 },
{ lat: 10, lng: 0 },
{ lat: 0, lng: 0 }
]);
});
it('creates a hull for vertically-aligned objects', function () {
expect(L.QuickHull.getConvexHull([ { lat: 0, lng: 0 },
{ lat: 5, lng: 0 },
{ lat: 10, lng: 0 }
])).to.eql([
{ lat: 0, lng: 0 },
{ lat: 10, lng: 0 }
]);
});
it('creates a hull for horizontally-aligned objects', function () {
expect(L.QuickHull.getConvexHull([ { lat: 0, lng: 0 },
{ lat: 0, lng: 5 },
{ lat: 0, lng: 10 }
])).to.eql([
{ lat: 0, lng: 0 },
{ lat: 0, lng: 10 }
]);
});
});
});
@@ -0,0 +1,481 @@
describe('refreshClusters', function () {
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
clock = sinon.useFakeTimers();
// Look away to avoid refreshing the display while adding markers.
// By adding markers one by one (instead of using addLayers) we make
// sure we never start an async process.
map.fitBounds(new L.LatLngBounds([
[-11, -11],
[-10, -10]
]))
});
afterEach(function () {
if (group instanceof L.MarkerClusterGroup) {
group.removeLayers(group.getLayers());
map.removeLayer(group);
}
// group must be thrown away since we are testing it with a potentially
// different configuration at each test.
group = null;
clock.restore();
clock = null;
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var div, map, group, clock;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
function getClusterAtZoom(marker, zoom) {
var parent = marker.__parent;
while (parent && parent._zoom !== zoom) {
parent = parent.__parent;
}
return parent;
}
function setMapView() {
// Now look at the markers to force cluster icons drawing.
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
}
/////////////////////////////
// TESTS
/////////////////////////////
it('flags all non-visible parent clusters of a given marker', function () {
group = L.markerClusterGroup().addTo(map);
var marker1 = L.marker([1.5, 1.5]).addTo(group),
marker2 = L.marker([1.5, 1.5]).addTo(group); // Needed to force a cluster.
setMapView();
var marker1cluster10 = getClusterAtZoom(marker1, 10),
marker1cluster2 = getClusterAtZoom(marker1, 2),
marker1cluster5 = getClusterAtZoom(marker1, 5);
// First go to some zoom levels so that Leaflet initializes clusters icons.
expect(marker1cluster10._iconNeedsUpdate).to.be.ok();
map.setZoom(10, {animate: false});
expect(marker1cluster10._iconNeedsUpdate).to.not.be.ok();
expect(marker1cluster2._iconNeedsUpdate).to.be.ok();
map.setZoom(2, {animate: false});
expect(marker1cluster2._iconNeedsUpdate).to.not.be.ok();
// Finish on an intermediate zoom level.
expect(marker1cluster5._iconNeedsUpdate).to.be.ok();
map.setZoom(5, {animate: false});
expect(marker1cluster5._iconNeedsUpdate).to.not.be.ok();
// Run any animation.
clock.tick(1000);
// Then request clusters refresh.
// No need to actually modify the marker.
group.refreshClusters(marker1);
// Now check that non-visible clusters are flagged as "dirty".
expect(marker1cluster10._iconNeedsUpdate).to.be.ok();
expect(marker1cluster2._iconNeedsUpdate).to.be.ok();
// Also check that visible clusters are "un-flagged" since they should be re-drawn.
expect(marker1cluster5._iconNeedsUpdate).to.not.be.ok();
});
it('re-draws visible clusters', function () {
group = L.markerClusterGroup({
iconCreateFunction: function (cluster) {
var markers = cluster.getAllChildMarkers();
for(var i in markers) {
if (markers[i].changed) {
return new L.DivIcon({
className: "changed"
});
}
}
return new L.DivIcon({
className: "original"
});
}
}).addTo(map);
var marker1 = L.marker([1.5, 1.5]).addTo(group),
marker2 = L.marker([1.5, 1.5]).addTo(group); // Needed to force a cluster.
setMapView();
var marker1cluster9 = getClusterAtZoom(marker1, 9);
// First go to some zoom levels so that Leaflet initializes clusters icons.
expect(marker1cluster9._iconNeedsUpdate).to.be.ok();
map.setZoom(9, {animate: false});
expect(marker1cluster9._iconNeedsUpdate).to.not.be.ok();
expect(marker1cluster9._icon.className).to.contain("original");
expect(marker1cluster9._icon.className).to.not.contain("changed");
// Run any animation.
clock.tick(1000);
// Alter the marker.
marker1.changed = true;
// Then request clusters refresh.
group.refreshClusters(marker1);
// Now check that visible clusters icon is re-drawn.
expect(marker1cluster9._icon.className).to.contain("changed");
expect(marker1cluster9._icon.className).to.not.contain("original");
});
// Shared code for the 2 below tests
function iconCreateFunction(cluster) {
var markers = cluster.getAllChildMarkers();
for(var i in markers) {
if (markers[i].changed) {
return new L.DivIcon({
className: "changed"
});
}
}
return new L.DivIcon({
className: "original"
});
}
it('re-draws markers in singleMarkerMode', function () {
group = L.markerClusterGroup({
singleMarkerMode: true,
iconCreateFunction: iconCreateFunction
}).addTo(map);
var marker1 = L.marker([1.5, 1.5]).addTo(group);
setMapView();
expect(marker1._icon.className).to.contain("original");
// Alter the marker.
marker1.changed = true;
// Then request clusters refresh.
group.refreshClusters(marker1);
expect(marker1._icon.className).to.contain("changed");
expect(marker1._icon.className).to.not.contain("original");
});
it('does not modify markers that do not belong to the current group (in singleMarkerMode)', function () {
group = L.markerClusterGroup({
singleMarkerMode: true,
iconCreateFunction: iconCreateFunction
}).addTo(map);
var marker1 = L.marker([1.5, 1.5]).addTo(group),
marker2 = L.marker([1.5, 1.5], {
icon: iconCreateFunction({
getAllChildMarkers: function () {
return marker2;
}
})
}).addTo(map);
setMapView();
expect(marker1._icon.className).to.contain("original");
expect(marker2._icon.className).to.contain("original");
// Alter the markers.
marker1.changed = true;
marker2.changed = true;
// Then request clusters refresh.
group.refreshClusters([marker1, marker2]);
expect(marker1._icon.className).to.contain("changed");
expect(marker1._icon.className).to.not.contain("original");
expect(marker2._icon.className).to.contain("original");
expect(marker2._icon.className).to.not.contain("changed");
});
// Shared code for below tests.
var marker1 = L.marker([1.5, 1.5]),
marker2 = L.marker([1.5, 1.5]), // Needed to force a cluster.
marker3 = L.marker([1.1, 1.1]),
marker4 = L.marker([1.1, 1.1]), // Needed to force a cluster.
marker5 = L.marker([1.9, 1.9]),
marker6 = L.marker([1.9, 1.9]), // Needed to force a cluster.
marker1cluster8,
marker1cluster3,
marker1cluster5,
marker3cluster8,
marker3cluster3,
marker3cluster5,
marker5cluster8,
marker5cluster3,
marker5cluster5;
function init3clusterBranches() {
group = L.markerClusterGroup({
maxClusterRadius: 2 // Make sure we keep distinct clusters.
}).addTo(map);
// Populate Marker Cluster Group.
marker1.addTo(group);
marker2.addTo(group);
marker3.addTo(group);
marker4.addTo(group);
marker5.addTo(group);
marker6.addTo(group);
setMapView();
marker1cluster8 = getClusterAtZoom(marker1, 8);
marker1cluster3 = getClusterAtZoom(marker1, 3);
marker1cluster5 = getClusterAtZoom(marker1, 5);
marker3cluster8 = getClusterAtZoom(marker3, 8);
marker3cluster3 = getClusterAtZoom(marker3, 3);
marker3cluster5 = getClusterAtZoom(marker3, 5);
marker5cluster8 = getClusterAtZoom(marker5, 8);
marker5cluster3 = getClusterAtZoom(marker5, 3);
marker5cluster5 = getClusterAtZoom(marker5, 5);
// Make sure we have 3 distinct clusters up to zoom level Z (let's choose Z = 3)
expect(marker1cluster3._childCount).to.equal(2);
expect(marker3cluster3._childCount).to.equal(2);
expect(marker5cluster3._childCount).to.equal(2);
// First go to some zoom levels so that Leaflet initializes clusters icons.
expect(marker1cluster8._iconNeedsUpdate).to.not.be.ok();
expect(marker3cluster8._iconNeedsUpdate).to.not.be.ok();
expect(marker5cluster8._iconNeedsUpdate).to.not.be.ok();
expect(marker1cluster3._iconNeedsUpdate).to.be.ok();
expect(marker3cluster3._iconNeedsUpdate).to.be.ok();
expect(marker5cluster3._iconNeedsUpdate).to.be.ok();
map.setZoom(3, {animate: false});
expect(marker1cluster3._iconNeedsUpdate).to.not.be.ok();
expect(marker3cluster3._iconNeedsUpdate).to.not.be.ok();
expect(marker5cluster3._iconNeedsUpdate).to.not.be.ok();
// Finish on an intermediate zoom level.
expect(marker1cluster5._iconNeedsUpdate).to.be.ok();
expect(marker3cluster5._iconNeedsUpdate).to.be.ok();
expect(marker5cluster5._iconNeedsUpdate).to.be.ok();
map.setZoom(5, {animate: false});
expect(marker1cluster5._iconNeedsUpdate).to.not.be.ok();
expect(marker3cluster5._iconNeedsUpdate).to.not.be.ok();
expect(marker5cluster5._iconNeedsUpdate).to.not.be.ok();
// Run any animation.
clock.tick(1000);
// Ready to refresh clusters with method of choice and assess result.
}
it('does not flag clusters of other markers', function () {
init3clusterBranches();
// Then request clusters refresh.
// No need to actually modify the marker.
group.refreshClusters(marker1);
// Now check that non-visible clusters are flagged as "dirty".
expect(marker1cluster8._iconNeedsUpdate).to.be.ok();
expect(marker1cluster3._iconNeedsUpdate).to.be.ok();
// Finally check that non-involved clusters are not "dirty".
expect(marker3cluster8._iconNeedsUpdate).to.not.be.ok();
expect(marker3cluster3._iconNeedsUpdate).to.not.be.ok();
expect(marker5cluster8._iconNeedsUpdate).to.not.be.ok();
expect(marker5cluster3._iconNeedsUpdate).to.not.be.ok();
});
it('processes itself when no argument is passed', function () {
init3clusterBranches();
// Then request clusters refresh.
// No need to actually modify the marker.
group.refreshClusters();
// Now check that non-visible clusters are flagged as "dirty".
expect(marker1cluster8._iconNeedsUpdate).to.be.ok();
expect(marker1cluster3._iconNeedsUpdate).to.be.ok();
expect(marker3cluster8._iconNeedsUpdate).to.be.ok();
expect(marker3cluster3._iconNeedsUpdate).to.be.ok();
expect(marker5cluster8._iconNeedsUpdate).to.be.ok();
expect(marker5cluster3._iconNeedsUpdate).to.be.ok();
});
it('accepts an array of markers', function () {
init3clusterBranches();
// Then request clusters refresh.
// No need to actually modify the markers.
group.refreshClusters([marker1, marker5]);
// Clusters of marker3 and 4 shall not be flagged.
// Now check that non-visible clusters are flagged as "dirty".
expect(marker1cluster8._iconNeedsUpdate).to.be.ok();
expect(marker1cluster3._iconNeedsUpdate).to.be.ok();
expect(marker5cluster8._iconNeedsUpdate).to.be.ok();
expect(marker5cluster3._iconNeedsUpdate).to.be.ok();
// Clusters of marker3 and 4 shall not be flagged.
expect(marker3cluster8._iconNeedsUpdate).to.not.be.ok();
expect(marker3cluster3._iconNeedsUpdate).to.not.be.ok();
});
it('accepts a mapping of markers', function () {
init3clusterBranches();
// Then request clusters refresh.
// No need to actually modify the markers.
group.refreshClusters({
id1: marker1,
id2: marker5
}); // Clusters of marker3 and 4 shall not be flagged.
// Now check that non-visible clusters are flagged as "dirty".
expect(marker1cluster8._iconNeedsUpdate).to.be.ok();
expect(marker1cluster3._iconNeedsUpdate).to.be.ok();
expect(marker5cluster8._iconNeedsUpdate).to.be.ok();
expect(marker5cluster3._iconNeedsUpdate).to.be.ok();
// Clusters of marker3 and 4 shall not be flagged.
expect(marker3cluster8._iconNeedsUpdate).to.not.be.ok();
expect(marker3cluster3._iconNeedsUpdate).to.not.be.ok();
});
it('accepts an L.LayerGroup', function () {
init3clusterBranches();
// Then request clusters refresh.
// No need to actually modify the markers.
var layerGroup = new L.LayerGroup([marker1, marker5]);
group.refreshClusters(layerGroup);
// Clusters of marker3 and 4 shall not be flagged.
// Now check that non-visible clusters are flagged as "dirty".
expect(marker1cluster8._iconNeedsUpdate).to.be.ok();
expect(marker1cluster3._iconNeedsUpdate).to.be.ok();
expect(marker5cluster8._iconNeedsUpdate).to.be.ok();
expect(marker5cluster3._iconNeedsUpdate).to.be.ok();
// Clusters of marker3 and 4 shall not be flagged.
expect(marker3cluster8._iconNeedsUpdate).to.not.be.ok();
expect(marker3cluster3._iconNeedsUpdate).to.not.be.ok();
});
it('accepts an L.MarkerCluster', function () {
init3clusterBranches();
// Then request clusters refresh.
// No need to actually modify the markers.
group.refreshClusters(marker1cluster8);
// Clusters of marker3, 4, 5 and 6 shall not be flagged.
// Now check that non-visible clusters are flagged as "dirty".
expect(marker1cluster8._iconNeedsUpdate).to.be.ok();
expect(marker1cluster3._iconNeedsUpdate).to.be.ok();
// Clusters of marker3 and 4 shall not be flagged.
expect(marker3cluster8._iconNeedsUpdate).to.not.be.ok();
expect(marker3cluster3._iconNeedsUpdate).to.not.be.ok();
expect(marker5cluster8._iconNeedsUpdate).to.not.be.ok();
expect(marker5cluster3._iconNeedsUpdate).to.not.be.ok();
});
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});
@@ -27,7 +27,7 @@ describe('Remember opacity', function () {
map = L.map(div, { maxZoom: 18 });
markers = [];
for (var i=0; i<markerDefs.length; i++) {
for (var i = 0; i < markerDefs.length; i++) {
markers.push( L.marker(markerDefs[i].latLng, markerDefs[i].opts ) );
}
});
@@ -1,27 +1,72 @@
describe('removeLayer', function () {
var map, div, clock;
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
clock = sinon.useFakeTimers();
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
});
afterEach(function () {
if (group instanceof L.MarkerClusterGroup) {
group.clearLayers();
map.removeLayer(group);
}
// Throw away group as it can be assigned with different configurations between tests.
group = null;
clock.restore();
document.body.removeChild(div);
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var div, map, group, clock;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
/////////////////////////////
// TESTS
/////////////////////////////
it('removes a layer that was added to it', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
map.addLayer(group);
@@ -37,7 +82,8 @@
it('doesnt remove a layer not added to it', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
map.addLayer(group);
@@ -53,7 +99,8 @@
it('removes a layer that was added to it (before being on the map) that is shown in a cluster', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
@@ -68,7 +115,8 @@
it('removes a layer that was added to it (after being on the map) that is shown in a cluster', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
@@ -84,7 +132,8 @@
it('removes a layer that was added to it (before being on the map) that is individually', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1, 1.5]);
var marker2 = new L.Marker([3, 1.5]);
@@ -103,7 +152,8 @@
it('removes a layer (with animation) that was added to it (after being on the map) that is shown in a cluster', function () {
var group = new L.MarkerClusterGroup({ animateAddingMarkers: true });
group = new L.MarkerClusterGroup({ animateAddingMarkers: true });
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
@@ -128,7 +178,8 @@
it('removes the layers that are in the given LayerGroup', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
@@ -145,7 +196,8 @@
it('removes the layers that are in the given LayerGroup when not on the map', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
@@ -158,4 +210,30 @@
expect(group.hasLayer(marker)).to.be(true);
expect(group.hasLayer(marker2)).to.be(false);
});
});
it('passes control to removeLayers when marker is a Layer Group', function () {
group = new L.MarkerClusterGroup();
var marker1 = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
group.addLayers([marker1, marker2]);
var layer = L.layerGroup();
layer.addLayer(marker2);
group.removeLayer(new L.LayerGroup([layer]));
expect(group.hasLayer(marker1)).to.be(true);
expect(group.hasLayer(marker2)).to.be(false);
});
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});
@@ -0,0 +1,147 @@
describe('animate option', function () {
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
previousTransitionSetting = L.DomUtil.TRANSITION;
});
afterEach(function () {
// Restore previous setting so that next tests are unaffected.
L.DomUtil.TRANSITION = previousTransitionSetting;
if (group instanceof L.MarkerClusterGroup) {
group.removeLayers(group.getLayers());
map.removeLayer(group);
}
// Throw away group as it can be assigned with different configurations between tests.
group = null;
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var div, map, group, previousTransitionSetting;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
/////////////////////////////
// TESTS
/////////////////////////////
it('hooks animated methods version by default', function () {
// Need to add to map so that we have the top cluster level created.
group = L.markerClusterGroup().addTo(map);
var withAnimation = L.MarkerClusterGroup.prototype._withAnimation;
// MCG animated methods.
expect(group._animationStart).to.be(withAnimation._animationStart);
expect(group._animationZoomIn).to.be(withAnimation._animationZoomIn);
expect(group._animationZoomOut).to.be(withAnimation._animationZoomOut);
expect(group._animationAddLayer).to.be(withAnimation._animationAddLayer);
// MarkerCluster spiderfy animated methods
var cluster = group._topClusterLevel;
withAnimation = L.MarkerCluster.prototype;
expect(cluster._animationSpiderfy).to.be(withAnimation._animationSpiderfy);
expect(cluster._animationUnspiderfy).to.be(withAnimation._animationUnspiderfy);
});
it('hooks non-animated methods version when set to false', function () {
// Need to add to map so that we have the top cluster level created.
group = L.markerClusterGroup({animate: false}).addTo(map);
var noAnimation = L.MarkerClusterGroup.prototype._noAnimation;
// MCG non-animated methods.
expect(group._animationStart).to.be(noAnimation._animationStart);
expect(group._animationZoomIn).to.be(noAnimation._animationZoomIn);
expect(group._animationZoomOut).to.be(noAnimation._animationZoomOut);
expect(group._animationAddLayer).to.be(noAnimation._animationAddLayer);
// MarkerCluster spiderfy non-animated methods
var cluster = group._topClusterLevel;
noAnimation = L.MarkerClusterNonAnimated.prototype;
expect(cluster._animationSpiderfy).to.be(noAnimation._animationSpiderfy);
expect(cluster._animationUnspiderfy).to.be(noAnimation._animationUnspiderfy);
});
it('always hooks non-animated methods version when L.DomUtil.TRANSITION is false', function () {
// Fool Leaflet, make it think the browser does not support transitions.
L.DomUtil.TRANSITION = false;
// Need to add to map so that we have the top cluster level created.
group = L.markerClusterGroup({animate: true}).addTo(map);
var noAnimation = L.MarkerClusterGroup.prototype._noAnimation;
// MCG non-animated methods.
expect(group._animationStart).to.be(noAnimation._animationStart);
expect(group._animationZoomIn).to.be(noAnimation._animationZoomIn);
expect(group._animationZoomOut).to.be(noAnimation._animationZoomOut);
expect(group._animationAddLayer).to.be(noAnimation._animationAddLayer);
// MarkerCluster spiderfy non-animated methods
var cluster = group._topClusterLevel;
noAnimation = L.MarkerClusterNonAnimated.prototype;
expect(cluster._animationSpiderfy).to.be(noAnimation._animationSpiderfy);
expect(cluster._animationUnspiderfy).to.be(noAnimation._animationUnspiderfy);
});
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});
@@ -0,0 +1,103 @@
describe('disableClusteringAtZoom option', function () {
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
clock = sinon.useFakeTimers();
});
afterEach(function () {
if (group instanceof L.MarkerClusterGroup) {
group.clearLayers();
map.removeLayer(group);
}
// group must be thrown away since we are testing it with a potentially
// different configuration at each test.
group = null;
clock.restore();
clock = null;
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var div, map, group, clock;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
/////////////////////////////
// TESTS
/////////////////////////////
it('unclusters at zoom level equal or higher', function () {
var maxZoom = 15;
group = new L.MarkerClusterGroup({
disableClusteringAtZoom: maxZoom
});
group.addLayers([
new L.Marker([1.5, 1.5]),
new L.Marker([1.5, 1.5])
]);
map.addLayer(group);
expect(group._maxZoom).to.equal(maxZoom - 1);
expect(map._panes.markerPane.childNodes.length).to.equal(1); // 1 cluster.
map.setZoom(14);
clock.tick(1000);
expect(map._panes.markerPane.childNodes.length).to.equal(1); // 1 cluster.
map.setZoom(15);
clock.tick(1000);
expect(map._panes.markerPane.childNodes.length).to.equal(2); // 2 markers.
});
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});
@@ -1,25 +1,71 @@
describe('events', function() {
var map, div;
beforeEach(function() {
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
//
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
});
afterEach(function() {
document.body.removeChild(div);
afterEach(function () {
if (group instanceof L.MarkerClusterGroup) {
group.removeLayers(group.getLayers());
map.removeLayer(group);
}
// group must be thrown away since we are testing it with a potentially
// different configuration at each test.
group = null;
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var div, map, group;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
/////////////////////////////
// TESTS
/////////////////////////////
it('is fired for a single child marker', function () {
var callback = sinon.spy();
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
@@ -27,14 +73,16 @@
group.addLayer(marker);
map.addLayer(group);
marker.fire('click');
// In Leaflet 1.0.0, event propagation must be explicitly set by 3rd argument.
marker.fire('click', null, true);
expect(callback.called).to.be(true);
});
it('is fired for a child polygon', function () {
var callback = sinon.spy();
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
@@ -42,14 +90,15 @@
group.addLayer(polygon);
map.addLayer(group);
polygon.fire('click');
polygon.fire('click', null, true);
expect(callback.called).to.be(true);
});
it('is fired for a cluster click', function () {
var callback = sinon.spy();
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
@@ -61,15 +110,17 @@
var cluster = group.getVisibleParent(marker);
expect(cluster instanceof L.MarkerCluster).to.be(true);
cluster.fire('click');
cluster.fire('click', null, true);
expect(callback.called).to.be(true);
});
describe('after being added, removed, re-added from the map', function() {
it('still fires events for nonpoint data', function() {
var callback = sinon.spy();
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
@@ -79,14 +130,15 @@
map.removeLayer(group);
map.addLayer(group);
polygon.fire('click');
polygon.fire('click', null, true);
expect(callback.called).to.be(true);
});
it('still fires events for point data', function() {
var callback = sinon.spy();
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
@@ -96,14 +148,15 @@
map.removeLayer(group);
map.addLayer(group);
marker.fire('click');
marker.fire('click', null, true);
expect(callback.called).to.be(true);
});
it('still fires cluster events', function() {
var callback = sinon.spy();
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
@@ -118,14 +171,15 @@
var cluster = group.getVisibleParent(marker);
expect(cluster instanceof L.MarkerCluster).to.be(true);
cluster.fire('click');
cluster.fire('click', null, true);
expect(callback.called).to.be(true);
});
it('doesnt break map events', function () {
it('does not break map events', function () {
var callback = sinon.spy();
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
map.on('zoomend', callback);
map.addLayer(group);
@@ -137,12 +191,200 @@
expect(callback.called).to.be(true);
});
//layeradd
it('fires layeradd when markers are added while not on the map', function() {
var callback = sinon.spy();
group = new L.MarkerClusterGroup();
group.on('layeradd', callback);
var marker = new L.Marker([1.5, 1.5]);
group.addLayer(marker);
expect(callback.callCount).to.be(1);
});
it('fires layeradd when vectors are added while not on the map', function() {
var callback = sinon.spy();
group = new L.MarkerClusterGroup();
group.on('layeradd', callback);
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayer(polygon);
expect(callback.callCount).to.be(1);
});
it('fires layeradd when markers are added while on the map', function() {
var callback = sinon.spy();
group = new L.MarkerClusterGroup();
group.on('layeradd', callback);
map.addLayer(group);
var marker = new L.Marker([1.5, 1.5]);
group.addLayer(marker);
expect(callback.callCount).to.be(1);
});
it('fires layeradd when vectors are added while on the map', function() {
var callback = sinon.spy();
group = new L.MarkerClusterGroup();
group.on('layeradd', callback);
map.addLayer(group);
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayer(polygon);
expect(callback.callCount).to.be(1);
});
it('fires layeradd when markers are added using addLayers while on the map with chunked loading', function() {
var callback = sinon.spy();
group = new L.MarkerClusterGroup({ chunkedLoading: true });
group.on('layeradd', callback);
map.addLayer(group);
var marker = new L.Marker([1.5, 1.5]);
group.addLayers([marker]);
expect(callback.callCount).to.be(1);
});
it('fires layeradd when vectors are added using addLayers while on the map with chunked loading', function() {
var callback = sinon.spy();
group = new L.MarkerClusterGroup({ chunkedLoading: true });
group.on('layeradd', callback);
map.addLayer(group);
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayers([polygon]);
expect(callback.callCount).to.be(1);
});
//layerremove
it('fires layerremove when a marker is removed while not on the map', function() {
var callback = sinon.spy();
group = new L.MarkerClusterGroup();
group.on('layerremove', callback);
var marker = new L.Marker([1.5, 1.5]);
group.addLayer(marker);
group.removeLayer(marker);
expect(callback.callCount).to.be(1);
});
it('fires layerremove when a vector is removed while not on the map', function() {
var callback = sinon.spy();
group = new L.MarkerClusterGroup();
group.on('layerremove', callback);
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayer(polygon);
group.removeLayer(polygon);
expect(callback.callCount).to.be(1);
});
it('fires layerremove when a marker is removed while on the map', function() {
var callback = sinon.spy();
group = new L.MarkerClusterGroup();
group.on('layerremove', callback);
map.addLayer(group);
var marker = new L.Marker([1.5, 1.5]);
group.addLayer(marker);
group.removeLayer(marker);
expect(callback.callCount).to.be(1);
});
it('fires layerremove when a vector is removed while on the map', function() {
var callback = sinon.spy();
group = new L.MarkerClusterGroup();
group.on('layerremove', callback);
map.addLayer(group);
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayer(polygon);
group.removeLayer(polygon);
expect(callback.callCount).to.be(1);
});
it('fires layerremove when a marker is removed using removeLayers while on the map with chunked loading', function() {
var callback = sinon.spy();
group = new L.MarkerClusterGroup({ chunkedLoading: true });
group.on('layerremove', callback);
map.addLayer(group);
var marker = new L.Marker([1.5, 1.5]);
group.addLayers([marker]);
group.removeLayers([marker]);
expect(callback.callCount).to.be(1);
});
it('fires layerremove when a vector is removed using removeLayers while on the map with chunked loading', function() {
var callback = sinon.spy();
group = new L.MarkerClusterGroup({ chunkedLoading: true });
group.on('layerremove', callback);
map.addLayer(group);
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayers([polygon]);
group.removeLayers([polygon]);
expect(callback.callCount).to.be(1);
});
it('fires layerremove when a marker is removed using removeLayers while not on the map with chunked loading', function() {
var callback = sinon.spy();
group = new L.MarkerClusterGroup({ chunkedLoading: true });
group.on('layerremove', callback);
var marker = new L.Marker([1.5, 1.5]);
group.addLayers([marker]);
group.removeLayers([marker]);
expect(callback.callCount).to.be(1);
});
it('fires layerremove when a vector is removed using removeLayers while not on the map with chunked loading', function() {
var callback = sinon.spy();
group = new L.MarkerClusterGroup({ chunkedLoading: true });
group.on('layerremove', callback);
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
group.addLayers([polygon]);
group.removeLayers([polygon]);
expect(callback.callCount).to.be(1);
});
});
/*
//No normal events can be fired by a clustered marker, so probably don't need this.
it('is fired for a clustered child marker', function() {
var callback = sinon.spy();
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
@@ -156,4 +398,12 @@
expect(callback.called).to.be(true);
});
*/
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});
@@ -45,4 +45,21 @@
expect(layers).to.contain(marker);
expect(layers).to.contain(polygon);
});
it('skips markers and polygons removed while not on the map', function () {
var group = new L.MarkerClusterGroup();
var polygon = new L.Polygon([[1.5, 1.5], [2.0, 1.5], [2.0, 2.0], [1.5, 2.0]]);
var marker = new L.Marker([1.5, 1.5]);
group.addLayers([polygon, marker]);
map.addLayer(group);
map.removeLayer(group);
group.removeLayers([polygon, marker]);
var layers = group.getLayers();
expect(layers.length).to.be(0);
});
});
@@ -0,0 +1,111 @@
describe('moving markers', function () {
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
clock = sinon.useFakeTimers();
});
afterEach(function () {
if (group instanceof L.MarkerClusterGroup) {
group.clearLayers();
map.removeLayer(group);
}
// Throw away group as it can be assigned with different configurations between tests.
group = null;
clock.restore();
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var div, map, group, clock;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
/////////////////////////////
// TESTS
/////////////////////////////
it('moves a marker that was moved while off the map', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([10, 10]);
map.addLayer(group);
group.addLayer(marker);
map.removeLayer(group);
marker.setLatLng([1.5, 1.5]);
map.addLayer(group);
expect(group.getLayers().length).to.be(1);
});
it('moves multiple markers that were moved while off the map', function () {
group = new L.MarkerClusterGroup();
map.addLayer(group);
var markers = [];
for (var i = 0; i < 10; i++) {
var marker = new L.Marker([10, 10]);
group.addLayer(marker);
markers.push(marker);
}
map.removeLayer(group);
for (var i = 0; i < 10; i++) {
var marker = markers[i];
marker.setLatLng([1.5, 1.5]);
}
map.addLayer(group);
expect(group.getLayers().length).to.be(10);
});
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});
@@ -0,0 +1,44 @@
describe('non-integer min/max zoom', function () {
var map, div, clock;
beforeEach(function () {
clock = sinon.useFakeTimers();
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { minZoom: 0.5, maxZoom: 18.5 });
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
});
afterEach(function () {
clock.restore();
document.body.removeChild(div);
});
it('dont break adding and removing markers', function () {
var group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
var marker3 = new L.Marker([1.5, 1.5]);
group.addLayer(marker);
group.addLayer(marker2);
map.addLayer(group);
group.addLayer(marker3);
expect(marker._icon).to.be(undefined);
expect(marker2._icon).to.be(undefined);
expect(marker3._icon).to.be(undefined);
expect(map._panes.markerPane.childNodes.length).to.be(1);
group.removeLayer(marker2);
});
});
@@ -1,27 +1,72 @@
describe('removeLayers', function () {
var map, div, clock;
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
clock = sinon.useFakeTimers();
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
});
afterEach(function () {
if (group instanceof L.MarkerClusterGroup) {
group.clearLayers();
map.removeLayer(group);
}
// Throw away group as it can be assigned with different configurations between tests.
group = null;
clock.restore();
document.body.removeChild(div);
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var div, map, group, clock;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
/////////////////////////////
// TESTS
/////////////////////////////
it('removes all the layer given to it', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var markers = [
new L.Marker([1.5, 1.5]),
new L.Marker([1.5, 1.5]),
@@ -41,10 +86,58 @@
expect(group.getLayers().length).to.be(0);
});
it('removes all the layer given to it even though they move', function () {
group = new L.MarkerClusterGroup();
var markers = [
new L.Marker([10, 10]),
new L.Marker([20, 20]),
new L.Marker([30, 30])
];
var len = markers.length;
map.addLayer(group);
group.addLayers(markers);
markers.forEach(function (marker) {
marker.setLatLng([1.5, 1.5]);
group.removeLayer(marker);
expect(group.getLayers().length).to.be(len - 1);
group.addLayer(marker);
expect(group.getLayers().length).to.be(len);
});
expect(group.getLayers().length).to.be(len);
});
it('removes all the layer given to it even if the group is not on the map', function () {
group = new L.MarkerClusterGroup();
var markers = [
new L.Marker([1.5, 1.5]),
new L.Marker([1.5, 1.5]),
new L.Marker([1.5, 1.5])
];
map.addLayer(group);
group.addLayers(markers);
map.removeLayer(group);
group.removeLayers(markers);
map.addLayer(group);
expect(group.hasLayer(markers[0])).to.be(false);
expect(group.hasLayer(markers[1])).to.be(false);
expect(group.hasLayer(markers[2])).to.be(false);
expect(group.getLayers().length).to.be(0);
});
it('doesnt break if we are spiderfied', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var markers = [
new L.Marker([1.5, 1.5]),
new L.Marker([1.5, 1.5]),
@@ -72,4 +165,45 @@
expect(group._spiderfied).to.be(null);
});
});
});
it('handles nested Layer Groups', function () {
group = new L.MarkerClusterGroup();
var marker1 = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
var marker3 = new L.Marker([1.5, 1.5]);
map.addLayer(group);
group.addLayers([marker1, marker2, marker3]);
expect(group.hasLayer(marker1)).to.be(true);
expect(group.hasLayer(marker2)).to.be(true);
expect(group.hasLayer(marker3)).to.be(true);
group.removeLayers([
marker1,
new L.LayerGroup([
marker2, new L.LayerGroup([
marker3
])
])
]);
expect(group.hasLayer(marker1)).to.be(false);
expect(group.hasLayer(marker2)).to.be(false);
expect(group.hasLayer(marker3)).to.be(false);
expect(group.getLayers().length).to.be(0);
});
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});
@@ -0,0 +1,278 @@
describe('Option removeOutsideVisibleBounds', function () {
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
// Nothing for this test suite.
clock = sinon.useFakeTimers();
});
afterEach(function () {
// Restore the previous setting, so that even in case of test failure, next tests are not affected.
L.Browser.mobile = previousMobileSetting;
if (group instanceof L.MarkerClusterGroup) {
//group.removeLayers(group.getLayers());
group.clearLayers();
map.removeLayer(group);
}
// group must be thrown away since we are testing it with a potentially
// different configuration at each test.
group = null;
clock.restore();
clock = null;
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var marker1 = L.marker([1.5, -0.4]), // 2 screens width away.
marker2 = L.marker([1.5, 0.6]), // 1 screen width away.
marker3 = L.marker([1.5, 1.5]), // In view port.
marker4 = L.marker([1.5, 2.4]), // 1 screen width away.
marker5 = L.marker([1.5, 3.4]), // 2 screens width away.
markers = [marker1, marker2, marker3, marker4, marker5],
previousMobileSetting = L.Browser.mobile,
div, map, group, i, clock;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
// Add all markers once to map then remove them immediately so that their icon is null (instead of undefined).
for (i = 0; i < markers.length; i++) {
map.removeLayer(markers[i].addTo(map));
}
function prepareGroup() {
// "group" should be assigned with a Marker Cluster Group before calling this function.
group.addTo(map);
group.addLayers(markers);
}
/////////////////////////////
// TESTS
/////////////////////////////
it('removes objects more than 1 screen away from view port by default', function () {
group = L.markerClusterGroup();
prepareGroup();
expect(marker1._icon).to.be(null);
expect(map._panes.markerPane.childNodes.length).to.be(3); // markers 2, 3 and 4.
expect(marker5._icon).to.be(null);
});
it('removes objects out of view port by default for mobile device', function () {
// Fool Leaflet, make it thinks it runs on a mobile device.
L.Browser.mobile = true;
group = L.markerClusterGroup();
prepareGroup();
expect(marker1._icon).to.be(null);
expect(marker2._icon).to.be(null);
expect(map._panes.markerPane.childNodes.length).to.be(1); // marker 3 only.
expect(marker4._icon).to.be(null);
expect(marker5._icon).to.be(null);
});
it('leaves all objects on map when set to false', function () {
group = L.markerClusterGroup({
removeOutsideVisibleBounds: false
});
prepareGroup();
expect(map._panes.markerPane.childNodes.length).to.be(5); // All 5 markers.
});
// Following tests need markers at very high latitude.
// They test the _checkBoundsMaxLat method against the default Web/Spherical Mercator projection maximum latitude (85.0511287798).
// The actual map view should be '-1.0986328125,84.92929204957956,1.0986328125,85.11983467698401'
// The expanded bounds without correction should be '-3.2958984375,84.7387494221751,3.2958984375,85.31037730438847'
var latLngsMaxLatDefault = [
[100, 3], // Impossible in real world, but nothing prevents the user from entering such latitude, and Web/Spherical Mercator projection will still display it at 85.0511287798
[85.2, 1.5], // 1 "screen" heights away.
[85, 0], // In center of view.
[84.8, -1.5], // 1 "screen" height away.
[84.6, -3] // 2 "screens" height away.
];
function moveMarkersAndMapToMaxLat(latLngs, isSouth) {
for (i = 0; i < markers.length; i++) {
if (isSouth) {
markers[i].setLatLng([-latLngs[i][0], latLngs[i][1]]);
} else {
markers[i].setLatLng(latLngs[i]);
}
}
map.fitBounds([
[isSouth ? -86 : 85, -1],
[isSouth ? -85 : 86, 1] // The actual map view longitude span will be wider. '-1.0986328125,84.92929204957956,1.0986328125,85.11983467698401'
]);
}
function checkProjection(latLngs) {
expect(map.options.crs).to.equal(L.CRS.EPSG3857);
expect(L.CRS.EPSG3857.projection).to.equal(L.Projection.SphericalMercator);
expect(L.Projection.SphericalMercator.MAX_LATITUDE).to.be.a('number');
var mapZoom = map.getZoom();
for (i = 0; i < markers.length; i++) {
try {
expect(markers[i].__parent._zoom).to.be.below(mapZoom);
} catch (e) {
console.log("Failed marker: " + (i + 1));
throw e;
}
}
}
it('includes objects above the Web Mercator projection maximum limit by default', function () {
moveMarkersAndMapToMaxLat(latLngsMaxLatDefault);
group = L.markerClusterGroup();
prepareGroup();
checkProjection(latLngsMaxLatDefault);
expect(map._panes.markerPane.childNodes.length).to.be(4); // Markers 1, 2, 3 and 4.
expect(marker5._icon).to.be(null);
});
it('includes objects below the Web Mercator projection minimum limit by default', function () {
moveMarkersAndMapToMaxLat(latLngsMaxLatDefault, true);
// Make sure we are really in Southern hemisphere.
expect(map.getBounds().getNorth()).to.be.below(-80);
group = L.markerClusterGroup();
prepareGroup();
checkProjection(latLngsMaxLatDefault);
clock.tick(1000);
expect(map._panes.markerPane.childNodes.length).to.be(4); // Markers 1, 2, 3 and 4.
expect(marker5._icon).to.be(null);
});
// The actual map view should be '-1.0986328125,84.92929204957956,1.0986328125,85.11983467698401'
var latLngsMaxLatMobile = [
[100, 1], // Impossible in real world, but nothing prevents the user from entering such latitude, and Web/Spherical Mercator projection will still display it at 85.0511287798
[85.2, 0.5], // 1 "screen" heights away, but should be included by the correction.
[85, 0], // In center of view.
[84.9, -1], // 1 "screen" height away.
[84.8, -1.5] // 2 "screens" height away.
];
it('includes objects above the Web Mercator projection maximum limit for mobile device', function () {
// Fool Leaflet, make it thinks it runs on a mobile device.
L.Browser.mobile = true;
moveMarkersAndMapToMaxLat(latLngsMaxLatMobile);
group = L.markerClusterGroup({
maxClusterRadius: 10
});
prepareGroup();
checkProjection(latLngsMaxLatMobile);
expect(map._panes.markerPane.childNodes.length).to.be(3); // Markers 1, 2 and 3.
expect(marker4._icon).to.be(null);
expect(marker5._icon).to.be(null);
});
it('includes objects below the Web Mercator projection minimum limit for mobile device', function () {
// Fool Leaflet, make it thinks it runs on a mobile device.
L.Browser.mobile = true;
moveMarkersAndMapToMaxLat(latLngsMaxLatMobile, true);
// Make sure we are really in Southern hemisphere.
expect(map.getBounds().getNorth()).to.be.below(-80);
group = L.markerClusterGroup({
maxClusterRadius: 10
});
prepareGroup();
checkProjection(latLngsMaxLatMobile);
expect(map._panes.markerPane.childNodes.length).to.be(3); // Markers 1, 2 and 3.
expect(marker4._icon).to.be(null);
expect(marker5._icon).to.be(null);
});
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});
@@ -0,0 +1,110 @@
describe('singleMarkerMode option', function () {
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
// Reset the marker icon.
marker.setIcon(defaultIcon);
});
afterEach(function () {
if (group instanceof L.MarkerClusterGroup) {
group.removeLayers(group.getLayers());
map.removeLayer(group);
}
// Throw away group as it can be assigned with different configurations between tests.
group = null;
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var div, map, group;
var defaultIcon = new L.Icon.Default(),
clusterIcon = new L.Icon.Default(),
marker = L.marker([1.5, 1.5]);
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
/////////////////////////////
// TESTS
/////////////////////////////
it('overrides marker icons when set to true', function () {
group = L.markerClusterGroup({
singleMarkerMode: true,
iconCreateFunction: function (layer) {
return clusterIcon;
}
}).addTo(map);
expect(marker.options.icon).to.equal(defaultIcon);
marker.addTo(group);
expect(marker.options.icon).to.equal(clusterIcon);
});
it('does not modify marker icons by default (or set to false)', function () {
group = L.markerClusterGroup({
iconCreateFunction: function (layer) {
return clusterIcon;
}
}).addTo(map);
expect(marker.options.icon).to.equal(defaultIcon);
marker.addTo(group);
expect(marker.options.icon).to.equal(defaultIcon);
});
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});
@@ -1,28 +1,74 @@
describe('spiderfy', function () {
var map, div, clock;
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
clock = sinon.useFakeTimers();
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
});
afterEach(function () {
if (group instanceof L.MarkerClusterGroup) {
group.removeLayers(group.getLayers());
map.removeLayer(group);
}
// group must be thrown away since we are testing it with a potentially
// different configuration at each test.
group = null;
clock.restore();
document.body.removeChild(div);
clock = null;
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var div, map, group, clock;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
/////////////////////////////
// TESTS
/////////////////////////////
it('Spiderfies 2 Markers', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
@@ -38,7 +84,8 @@
it('Spiderfies 2 CircleMarkers', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var marker = new L.CircleMarker([1.5, 1.5]);
var marker2 = new L.CircleMarker([1.5, 1.5]);
@@ -48,13 +95,16 @@
marker.__parent.spiderfy();
expect(marker._container.parentNode).to.be(map._pathRoot);
expect(marker2._container.parentNode).to.be(map._pathRoot);
// Leaflet 1.0.0 now uses an intermediate L.Renderer.
// marker > _path > _rootGroup (g) > _container (svg) > pane (div)
expect(marker._path.parentNode.parentNode.parentNode).to.be(map.getPane('overlayPane'));
expect(marker2._path.parentNode.parentNode.parentNode).to.be(map.getPane('overlayPane'));
});
it('Spiderfies 2 Circles', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var marker = new L.Circle([1.5, 1.5], 10);
var marker2 = new L.Circle([1.5, 1.5], 10);
@@ -64,14 +114,191 @@
marker.__parent.spiderfy();
expect(marker._container.parentNode).to.be(map._pathRoot);
expect(marker2._container.parentNode).to.be(map._pathRoot);
expect(marker._path.parentNode.parentNode.parentNode).to.be(map.getPane('overlayPane'));
expect(marker2._path.parentNode.parentNode.parentNode).to.be(map.getPane('overlayPane'));
});
it('Spiderfies at current zoom if all child markers are at the exact same position', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
group.addLayers([marker, marker2]);
map.addLayer(group);
// Get the appropriate cluster.
var cluster = marker.__parent,
zoom = map.getZoom();
while (cluster._zoom !== zoom) {
cluster = cluster.__parent;
}
expect(zoom).to.be.lessThan(10);
cluster.fireEvent('click', null, true);
clock.tick(1000);
expect(map.getZoom()).to.equal(zoom);
expect(marker._icon.parentNode).to.be(map._panes.markerPane);
expect(marker2._icon.parentNode).to.be(map._panes.markerPane);
});
it('Spiderfies at current zoom if all child markers are still within a single cluster at map maxZoom', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.50001]);
var marker2 = new L.Marker([1.5, 1.5]);
group.addLayers([marker, marker2]);
map.addLayer(group);
expect(marker.__parent._zoom).to.equal(18);
// Get the appropriate cluster.
var cluster = marker.__parent,
zoom = map.getZoom();
while (cluster._zoom !== zoom) {
cluster = cluster.__parent;
}
expect(zoom).to.be.lessThan(10);
cluster.fireEvent('click', null, true);
clock.tick(1000);
expect(map.getZoom()).to.equal(zoom);
expect(marker._icon.parentNode).to.be(map._panes.markerPane);
expect(marker2._icon.parentNode).to.be(map._panes.markerPane);
});
it('removes all markers and spider legs when group is removed from map', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
group.addLayers([marker, marker2]);
map.addLayer(group);
marker.__parent.spiderfy();
expect(map._panes.markerPane.childNodes.length).to.be(3); // The 2 markers + semi-transparent cluster.
expect(map.getPane('overlayPane').firstChild.firstChild.childNodes.length).to.be(2); // The 2 spider legs.
});
it('adds then removes class "leaflet-cluster-anim" from mapPane on spiderfy', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
group.addLayers([marker, marker2]);
map.addLayer(group);
marker.__parent.spiderfy();
expect(map._panes.mapPane.className).to.contain('leaflet-cluster-anim');
clock.tick(1000);
expect(map._panes.mapPane.className).to.not.contain('leaflet-cluster-anim');
});
it('adds then removes class "leaflet-cluster-anim" from mapPane on unspiderfy', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
group.addLayers([marker, marker2]);
map.addLayer(group);
marker.__parent.spiderfy();
clock.tick(1000);
marker.__parent.unspiderfy();
expect(map._panes.mapPane.className).to.contain('leaflet-cluster-anim');
clock.tick(1000);
expect(map._panes.mapPane.className).to.not.contain('leaflet-cluster-anim');
});
it('fires unspiderfied event on unspiderfy', function (done) {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
group.addLayers([marker, marker2]);
map.addLayer(group);
marker.__parent.spiderfy();
clock.tick(1000);
// Add event listener
group.on('unspiderfied', function (event) {
expect(event.target).to.be(group);
expect(event.cluster).to.be.a(L.Marker);
expect(event.markers[1]).to.be(marker);
expect(event.markers[0]).to.be(marker2);
done();
});
marker.__parent.unspiderfy();
clock.tick(1000);
});
it('does not leave class "leaflet-cluster-anim" on mapPane when group is removed while spiderfied', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
group.addLayers([marker, marker2]);
map.addLayer(group);
marker.__parent.spiderfy();
clock.tick(1000);
map.removeLayer(group);
expect(map._panes.mapPane.className).to.not.contain('leaflet-cluster-anim');
});
describe('zoomend event listener', function () {
it('unspiderfies correctly', function () {
var group = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var marker = new L.Circle([1.5, 1.5], 10);
var marker2 = new L.Circle([1.5, 1.5], 10);
@@ -88,5 +315,67 @@
//We should unspiderfy with no animation, so this should be null
expect(group._spiderfied).to.be(null);
});
});
});
describe('spiderfied event listener', function () {
it('Spiderfies 2 Markers', function (done) {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
group.addLayer(marker);
group.addLayer(marker2);
map.addLayer(group);
// Add event listener
group.on('spiderfied', function (event) {
expect(event.target).to.be(group);
expect(event.cluster).to.be.a(L.Marker);
expect(event.markers[1]).to.be(marker);
expect(event.markers[0]).to.be(marker2);
done();
});
marker.__parent.spiderfy();
clock.tick(200);
});
it('Spiderfies 2 Circles', function (done) {
group = new L.MarkerClusterGroup();
var marker = new L.Circle([1.5, 1.5], 10);
var marker2 = new L.Circle([1.5, 1.5], 10);
group.addLayer(marker);
group.addLayer(marker2);
map.addLayer(group);
// Add event listener
group.on('spiderfied', function (event) {
expect(event.target).to.be(group);
expect(event.cluster).to.be.a(L.Marker);
expect(event.markers[1]).to.be(marker);
expect(event.markers[0]).to.be(marker2);
done();
});
marker.__parent.spiderfy();
clock.tick(200);
});
});
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});
@@ -0,0 +1,124 @@
describe('things behave correctly with negative minZoom', function () {
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
// Nothing for this test suite.
});
afterEach(function () {
if (group instanceof L.MarkerClusterGroup) {
group.clearLayers();
map.removeLayer(group);
}
// Throw away group as it can be assigned with different configurations between tests.
group = null;
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var div, map, group;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { minZoom: -3, maxZoom: 18 });
map.setView(L.latLng(0, 0), -3);
/////////////////////////////
// TESTS
/////////////////////////////
it('shows a single marker added to the group before the group is added to the map', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
group.addLayer(marker);
map.addLayer(group);
expect(marker._icon).to.not.be(undefined);
expect(marker._icon.parentNode).to.be(map._panes.markerPane);
});
it('shows a single marker added to the group after the group is added to the map', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
map.addLayer(group);
group.addLayer(marker);
expect(marker._icon).to.not.be(undefined);
expect(marker._icon.parentNode).to.be(map._panes.markerPane);
});
it('creates a cluster when 2 overlapping markers are added before the group is added to the map', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
group.addLayer(marker);
group.addLayer(marker2);
map.addLayer(group);
expect(marker._icon).to.be(undefined);
expect(marker2._icon).to.be(undefined);
expect(map._panes.markerPane.childNodes.length).to.be(1);
});
it('creates a cluster when 2 overlapping markers are added after the group is added to the map', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
map.addLayer(group);
group.addLayer(marker);
group.addLayer(marker2);
expect(marker._icon).to.be(null); //Null as was added and then removed
expect(marker2._icon).to.be(undefined);
expect(map._panes.markerPane.childNodes.length).to.be(1);
});
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});
@@ -0,0 +1,169 @@
describe('unspiderfy', function () {
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
clock = sinon.useFakeTimers();
});
afterEach(function () {
if (group instanceof L.MarkerClusterGroup) {
group.removeLayers(group.getLayers());
map.removeLayer(group);
}
// group must be thrown away since we are testing it with a potentially
// different configuration at each test.
group = null;
clock.restore();
clock = null;
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var div, map, group, clock;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, {
maxZoom: 18
});
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
/////////////////////////////
// TESTS
/////////////////////////////
it('Unspiderfies 2 Markers', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
group.addLayer(marker);
group.addLayer(marker2);
map.addLayer(group);
marker.__parent.spiderfy();
clock.tick(1000);
group.unspiderfy();
clock.tick(1000);
expect(map.hasLayer(marker)).to.be(false);
expect(map.hasLayer(marker2)).to.be(false);
});
it('Unspiderfies 2 CircleMarkers', function () {
group = new L.MarkerClusterGroup();
var marker = new L.CircleMarker([1.5, 1.5]);
var marker2 = new L.CircleMarker([1.5, 1.5]);
group.addLayer(marker);
group.addLayer(marker2);
map.addLayer(group);
marker.__parent.spiderfy();
clock.tick(1000);
group.unspiderfy();
clock.tick(1000);
expect(map.hasLayer(marker)).to.be(false);
expect(map.hasLayer(marker2)).to.be(false);
});
it('Unspiderfies 2 Circles', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Circle([1.5, 1.5], 10);
var marker2 = new L.Circle([1.5, 1.5], 10);
group.addLayer(marker);
group.addLayer(marker2);
map.addLayer(group);
marker.__parent.spiderfy();
clock.tick(1000);
group.unspiderfy();
clock.tick(1000);
expect(map.hasLayer(marker)).to.be(false);
expect(map.hasLayer(marker2)).to.be(false);
});
it('fires unspiderfied event on unspiderfy', function (done) {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([1.5, 1.5]);
var marker2 = new L.Marker([1.5, 1.5]);
group.addLayers([marker, marker2]);
map.addLayer(group);
marker.__parent.spiderfy();
clock.tick(1000);
// Add event listener
group.on('unspiderfied', function (event) {
expect(event.target).to.be(group);
expect(event.cluster).to.be.a(L.Marker);
expect(event.markers[1]).to.be(marker);
expect(event.markers[0]).to.be(marker2);
done();
});
group.unspiderfy();
clock.tick(1000);
});
});
@@ -1,33 +1,86 @@
describe('zoomAnimation', function () {
var map, div, clock;
/**
* Avoid as much as possible creating and destroying objects for each test.
* Instead, try re-using them, except for the ones under test of course.
* PhantomJS does not perform garbage collection for the life of the page,
* i.e. during the entire test process (Karma runs all tests in a single page).
* http://stackoverflow.com/questions/27239708/how-to-get-around-memory-error-with-karma-phantomjs
*
* The `beforeEach` and `afterEach do not seem to cause much issue.
* => they can still be used to initialize some setup between each test.
* Using them keeps a readable spec/index.
*
* But refrain from re-creating div and map every time. Re-use those objects.
*/
/////////////////////////////
// SETUP FOR EACH TEST
/////////////////////////////
beforeEach(function () {
clock = sinon.useFakeTimers();
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
});
afterEach(function () {
clock.restore();
document.body.removeChild(div);
// Restore the previous setting, so that even in case of test failure, next tests are not affected.
L.Browser.mobile = previousMobileSetting;
if (group instanceof L.MarkerClusterGroup) {
group.clearLayers();
map.removeLayer(group);
}
// group must be thrown away since we are testing it with a potentially
// different configuration at each test.
group = null;
clock.restore();
clock = null;
});
/////////////////////////////
// PREPARATION CODE
/////////////////////////////
var previousMobileSetting = L.Browser.mobile,
div, map, group, clock;
div = document.createElement('div');
div.style.width = '200px';
div.style.height = '200px';
document.body.appendChild(div);
map = L.map(div, { maxZoom: 18 });
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
/////////////////////////////
// TESTS
/////////////////////////////
it('adds the visible marker to the map when zooming in', function () {
map.setView(new L.LatLng(-37.36142550190516, 174.254150390625), 7);
var markers = new L.MarkerClusterGroup({
group = new L.MarkerClusterGroup({
showCoverageOnHover: true,
maxClusterRadius: 20,
disableClusteringAtZoom: 15
});
var marker = new L.Marker([-37.77852090603777, 175.3103667497635]);
markers.addLayer(marker); //The one we zoom in on
markers.addLayer(new L.Marker([-37.711800591811055, 174.50034790039062])); //Marker that we cluster with at the top zoom level, but not 1 level down
map.addLayer(markers);
group.addLayer(marker); //The one we zoom in on
group.addLayer(new L.Marker([-37.711800591811055, 174.50034790039062])); //Marker that we cluster with at the top zoom level, but not 1 level down
map.addLayer(group);
clock.tick(1000);
map.setView([-37.77852090603777, 175.3103667497635], 15);
@@ -41,12 +94,12 @@
it('adds the visible marker to the map when jumping around', function () {
var markers = new L.MarkerClusterGroup();
group = new L.MarkerClusterGroup();
var marker1 = new L.Marker([48.858280181884766, 2.2945759296417236]);
var marker2 = new L.Marker([16.02359962463379, -61.70280075073242]);
markers.addLayer(marker1); //The one we zoom in on first
markers.addLayer(marker2); //Marker that we cluster with at the top zoom level, but not 1 level down
map.addLayer(markers);
group.addLayer(marker1); //The one we zoom in on first
group.addLayer(marker2); //Marker that we cluster with at the top zoom level, but not 1 level down
map.addLayer(group);
//show the first
map.fitBounds(new L.LatLngBounds(new L.LatLng(41.371582, -5.142222), new L.LatLng(51.092804, 9.561556)));
@@ -65,15 +118,16 @@
it('adds the visible markers to the map, but not parent clusters when jumping around', function () {
var markers = new L.MarkerClusterGroup(),
marker1 = new L.Marker([59.9520, 30.3307]),
group = new L.MarkerClusterGroup();
var marker1 = new L.Marker([59.9520, 30.3307]),
marker2 = new L.Marker([59.9516, 30.3308]),
marker3 = new L.Marker([59.9513, 30.3312]);
markers.addLayer(marker1);
markers.addLayer(marker2);
markers.addLayer(marker3);
map.addLayer(markers);
group.addLayer(marker1);
group.addLayer(marker2);
group.addLayer(marker3);
map.addLayer(group);
//Show none of them
map.setView([53.0676, 170.6835], 16);
@@ -92,4 +146,265 @@
expect(marker3._icon.parentNode).to.be(map._panes.markerPane);
expect(map._panes.markerPane.childNodes.length).to.be(3);
});
});
it('removes clicked clusters on the edge of a mobile screen', function () {
L.Browser.mobile = true;
// Corresponds to zoom level 8 for the above div dimensions.
map.fitBounds(new L.LatLngBounds([
[1, 1],
[2, 2]
]));
group = new L.MarkerClusterGroup({
maxClusterRadius: 80
}).addTo(map);
// Add a marker 1 pixel below the initial screen bottom edge.
var bottomPoint = map.getPixelBounds().max.add([0, 1]),
bottomLatLng = map.unproject(bottomPoint),
centerLng = map.getCenter().lng,
bottomPosition = new L.LatLng(
bottomLatLng.lat,
centerLng
),
bottomMarker = new L.Marker(bottomPosition).addTo(group),
initialZoom = map.getZoom();
expect(bottomMarker._icon).to.be(undefined);
// Add many markers 79 pixels above the first one, so they cluster with it.
var newPoint = bottomPoint.add([0, -79]),
newLatLng = L.latLng(
map.unproject(newPoint).lat,
centerLng
);
for (var i = 0; i < 10; i += 1) {
group.addLayer(new L.Marker(newLatLng));
}
var parentCluster = bottomMarker.__parent;
expect(parentCluster._icon.parentNode).to.be(map._panes.markerPane);
parentCluster.fireEvent('click', null, true);
//Run the the animation
clock.tick(1000);
expect(map.getZoom()).to.equal(initialZoom + 1); // The fitBounds with 200px height should result in zooming 1 level in.
// Finally make sure that the cluster has been removed from map.
expect(parentCluster._icon).to.be(null);
expect(map._panes.markerPane.childNodes.length).to.be(2); // The bottomMarker + cluster for the 10 above markers.
});
describe('zoomToShowLayer', function () {
it('zoom to single marker inside map view', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([59.9520, 30.3307]);
group.addLayer(marker);
map.addLayer(group);
var zoomCallbackSpy = sinon.spy();
map.setView(marker.getLatLng(), 10);
clock.tick(1000);
var initialCenter = map.getCenter();
var initialZoom = map.getZoom();
group.zoomToShowLayer(marker, zoomCallbackSpy);
//Run the the animation
clock.tick(1000);
//Marker should be visible, map center and zoom level should stay the same, callback called once
expect(marker._icon).to.not.be(undefined);
expect(marker._icon).to.not.be(null);
expect(map.getBounds().contains(marker.getLatLng())).to.be.true;
expect(map.getCenter()).to.eql(initialCenter);
expect(map.getZoom()).to.equal(initialZoom);
sinon.assert.calledOnce(zoomCallbackSpy);
});
it('pan map to single marker outside map view', function () {
group = new L.MarkerClusterGroup();
var marker = new L.Marker([59.9520, 30.3307]);
group.addLayer(marker);
map.addLayer(group);
var zoomCallbackSpy = sinon.spy();
//Show none of them
map.setView([53.0676, 170.6835], 16);
clock.tick(1000);
var initialZoom = map.getZoom();
group.zoomToShowLayer(marker, zoomCallbackSpy);
//Run the the animation
clock.tick(1000);
//Marker should be visible, map center should be equal to marker center,
//zoom level should stay the same, callback called once
expect(marker._icon).to.not.be(undefined);
expect(marker._icon).to.not.be(null);
expect(map.getBounds().contains(marker.getLatLng())).to.be.true;
expect(map.getCenter()).to.eql(marker.getLatLng());
expect(map.getZoom()).to.equal(initialZoom);
sinon.assert.calledOnce(zoomCallbackSpy);
});
it('change view and zoom to marker in cluster inside map view', function () {
group = new L.MarkerClusterGroup();
var marker1 = new L.Marker([59.9520, 30.3307]);
var marker2 = new L.Marker([59.9516, 30.3308]);
var marker3 = new L.Marker([59.9513, 30.3312]);
group.addLayer(marker1);
group.addLayer(marker2);
group.addLayer(marker3);
map.addLayer(group);
var zoomCallbackSpy = sinon.spy();
map.setView(marker1.getLatLng(), 16);
clock.tick(1000);
group.zoomToShowLayer(marker1, zoomCallbackSpy);
//Run the the animation
clock.tick(1000);
//Now the markers should all be visible, there should be no visible clusters, and callback called once
expect(marker1._icon.parentNode).to.be(map._panes.markerPane);
expect(marker2._icon.parentNode).to.be(map._panes.markerPane);
expect(marker3._icon.parentNode).to.be(map._panes.markerPane);
expect(map._panes.markerPane.childNodes.length).to.be(3);
expect(map.getBounds().contains(marker1.getLatLng())).to.be.true;
sinon.assert.calledOnce(zoomCallbackSpy);
});
it('change view and zoom to marker in cluster outside map view', function () {
group = new L.MarkerClusterGroup();
var marker1 = new L.Marker([59.9520, 30.3307]);
var marker2 = new L.Marker([59.9516, 30.3308]);
var marker3 = new L.Marker([59.9513, 30.3312]);
group.addLayer(marker1);
group.addLayer(marker2);
group.addLayer(marker3);
map.addLayer(group);
var zoomCallbackSpy = sinon.spy();
//Show none of them
map.setView([53.0676, 170.6835], 16);
clock.tick(1000);
group.zoomToShowLayer(marker1, zoomCallbackSpy);
//Run the the animation
clock.tick(1000);
//Now the markers should all be visible, there should be no visible clusters, and callback called once
expect(marker1._icon.parentNode).to.be(map._panes.markerPane);
expect(marker2._icon.parentNode).to.be(map._panes.markerPane);
expect(marker3._icon.parentNode).to.be(map._panes.markerPane);
expect(map._panes.markerPane.childNodes.length).to.be(3);
expect(map.getBounds().contains(marker1.getLatLng())).to.be.true;
sinon.assert.calledOnce(zoomCallbackSpy);
});
it('spiderfy overlapping markers', function () {
group = new L.MarkerClusterGroup();
var marker1 = new L.Marker([59.9520, 30.3307]);
var marker2 = new L.Marker([59.9520, 30.3307]);
var marker3 = new L.Marker([59.9520, 30.3307]);
group.addLayer(marker1);
group.addLayer(marker2);
group.addLayer(marker3);
map.addLayer(group);
var zoomCallbackSpy = sinon.spy();
//Show none of them
map.setView([53.0676, 170.6835], 16);
clock.tick(1000);
group.zoomToShowLayer(marker1, zoomCallbackSpy);
//Run the the animation
clock.tick(1000);
//Now the markers should all be visible, parent cluster should be spiderfied, and callback called once
expect(marker1._icon.parentNode).to.be(map._panes.markerPane);
expect(marker2._icon.parentNode).to.be(map._panes.markerPane);
expect(marker3._icon.parentNode).to.be(map._panes.markerPane);
expect(map._panes.markerPane.childNodes.length).to.be(4);//3 markers + spiderfied parent cluster
sinon.assert.calledOnce(zoomCallbackSpy);
});
it('zoom or spiderfy markers if they visible on next level of zoom', function () {
group = new L.MarkerClusterGroup();
var marker1 = new L.Marker([59.9520, 30.3307]);
var marker2 = new L.Marker([59.9516, 30.3308]);
var marker3 = new L.Marker([59.9513, 30.3312]);
group.addLayer(marker1);
group.addLayer(marker2);
group.addLayer(marker3);
map.addLayer(group);
var zoomCallbackSpy = sinon.spy();
//Markers will be visible on zoom 18
map.setView([59.9520, 30.3307], 17);
clock.tick(1000);
group.zoomToShowLayer(marker1, zoomCallbackSpy);
//Run the the animation
clock.tick(1000);
//Now the markers should all be visible (zoomed or spiderfied), and callback called once
expect(marker1._icon).to.not.be(undefined);
expect(marker1._icon).to.not.be(null);
expect(marker2._icon).to.not.be(undefined);
expect(marker2._icon).to.not.be(null);
expect(marker3._icon).to.not.be(undefined);
expect(marker3._icon).to.not.be(null);
sinon.assert.calledOnce(zoomCallbackSpy);
});
});
/////////////////////////////
// CLEAN UP CODE
/////////////////////////////
map.remove();
document.body.removeChild(div);
});
@@ -92,7 +92,8 @@ L.DistanceGrid.prototype = {
for (k = 0, len = cell.length; k < len; k++) {
obj = cell[k];
dist = this._sqDist(objectPoint[L.Util.stamp(obj)], point);
if (dist < closestDistSq) {
if (dist < closestDistSq ||
dist <= closestDistSq && closest === null) {
closestDistSq = dist;
closest = obj;
}
@@ -105,7 +106,8 @@ L.DistanceGrid.prototype = {
},
_getCoord: function (x) {
return Math.floor(x / this._cellSize);
var coord = Math.floor(x / this._cellSize);
return isFinite(coord) ? coord : x;
},
_sqDist: function (p, p2) {
@@ -108,20 +108,40 @@ Retrieved from: http://en.literateprograms.org/Quickhull_(Javascript)?oldid=1843
getConvexHull: function (latLngs) {
// find first baseline
var maxLat = false, minLat = false,
maxLng = false, minLng = false,
maxLatPt = null, minLatPt = null,
maxLngPt = null, minLngPt = null,
maxPt = null, minPt = null,
i;
for (i = latLngs.length - 1; i >= 0; i--) {
var pt = latLngs[i];
if (maxLat === false || pt.lat > maxLat) {
maxPt = pt;
maxLatPt = pt;
maxLat = pt.lat;
}
if (minLat === false || pt.lat < minLat) {
minPt = pt;
minLatPt = pt;
minLat = pt.lat;
}
if (maxLng === false || pt.lng > maxLng) {
maxLngPt = pt;
maxLng = pt.lng;
}
if (minLng === false || pt.lng < minLng) {
minLngPt = pt;
minLng = pt.lng;
}
}
if (minLat !== maxLat) {
minPt = minLatPt;
maxPt = maxLatPt;
} else {
minPt = minLngPt;
maxPt = maxLngPt;
}
var ch = [].concat(this.buildConvexHull([minPt, maxPt], latLngs),
this.buildConvexHull([maxPt, minPt], latLngs));
return ch;
@@ -5,7 +5,7 @@ L.MarkerCluster.include({
_2PI: Math.PI * 2,
_circleFootSeparation: 25, //related to circumference of circle
_circleStartAngle: Math.PI / 6,
_circleStartAngle: 0,
_spiralFootSeparation: 28, //related to size of spiral (experiment!)
_spiralLengthStart: 11,
@@ -33,7 +33,7 @@ L.MarkerCluster.include({
if (childMarkers.length >= this._circleSpiralSwitchover) {
positions = this._generatePointsSpiral(childMarkers.length, center);
} else {
center.y += 10; //Otherwise circles look wrong
center.y += 10; // Otherwise circles look wrong => hack for standard blue icon, renders differently for other icons.
positions = this._generatePointsCircle(childMarkers.length, center);
}
@@ -57,9 +57,11 @@ L.MarkerCluster.include({
res = [],
i, angle;
legLength = Math.max(legLength, 35); // Minimum distance to get outside the cluster icon.
res.length = count;
for (i = count - 1; i >= 0; i--) {
for (i = 0; i < count; i++) { // Clockwise, like spiral.
angle = this._circleStartAngle + i * angleStep;
res[i] = new L.Point(centerPt.x + legLength * Math.cos(angle), centerPt.y + legLength * Math.sin(angle))._round();
}
@@ -68,19 +70,25 @@ L.MarkerCluster.include({
},
_generatePointsSpiral: function (count, centerPt) {
var legLength = this._group.options.spiderfyDistanceMultiplier * this._spiralLengthStart,
separation = this._group.options.spiderfyDistanceMultiplier * this._spiralFootSeparation,
lengthFactor = this._group.options.spiderfyDistanceMultiplier * this._spiralLengthFactor,
var spiderfyDistanceMultiplier = this._group.options.spiderfyDistanceMultiplier,
legLength = spiderfyDistanceMultiplier * this._spiralLengthStart,
separation = spiderfyDistanceMultiplier * this._spiralFootSeparation,
lengthFactor = spiderfyDistanceMultiplier * this._spiralLengthFactor * this._2PI,
angle = 0,
res = [],
i;
res.length = count;
for (i = count - 1; i >= 0; i--) {
// Higher index, closer position to cluster center.
for (i = count; i >= 0; i--) {
// Skip the first position, so that we are already farther from center and we avoid
// being under the default cluster icon (especially important for Circle Markers).
if (i < count) {
res[i] = new L.Point(centerPt.x + legLength * Math.cos(angle), centerPt.y + legLength * Math.sin(angle))._round();
}
angle += separation / legLength + i * 0.0005;
res[i] = new L.Point(centerPt.x + legLength * Math.cos(angle), centerPt.y + legLength * Math.sin(angle))._round();
legLength += this._2PI * lengthFactor / angle;
legLength += lengthFactor / angle;
}
return res;
},
@@ -92,6 +100,8 @@ L.MarkerCluster.include({
childMarkers = this.getAllChildMarkers(),
m, i;
group._ignoreMove = true;
this.setOpacity(1);
for (i = childMarkers.length - 1; i >= 0; i--) {
m = childMarkers[i];
@@ -112,22 +122,38 @@ L.MarkerCluster.include({
}
}
group.fire('unspiderfied', {
cluster: this,
markers: childMarkers
});
group._ignoreMove = false;
group._spiderfied = null;
}
});
L.MarkerCluster.include(!L.DomUtil.TRANSITION ? {
//Non Animated versions of everything
//Non Animated versions of everything
L.MarkerClusterNonAnimated = L.MarkerCluster.extend({
_animationSpiderfy: function (childMarkers, positions) {
var group = this._group,
map = group._map,
fg = group._featureGroup,
legOptions = this._group.options.spiderLegPolylineOptions,
i, m, leg, newPos;
for (i = childMarkers.length - 1; i >= 0; i--) {
group._ignoreMove = true;
// Traverse in ascending order to make sure that inner circleMarkers are on top of further legs. Normal markers are re-ordered by newPosition.
// The reverse order trick no longer improves performance on modern browsers.
for (i = 0; i < childMarkers.length; i++) {
newPos = map.layerPointToLatLng(positions[i]);
m = childMarkers[i];
// Add the leg before the marker, so that in case the latter is a circleMarker, the leg is behind it.
leg = new L.Polyline([this._latlng, newPos], legOptions);
map.addLayer(leg);
m._spiderLeg = leg;
// Now add the marker.
m._preSpiderfyLatlng = m._latlng;
m.setLatLng(newPos);
if (m.setZIndexOffset) {
@@ -135,58 +161,95 @@ L.MarkerCluster.include(!L.DomUtil.TRANSITION ? {
}
fg.addLayer(m);
var legOptions = this._group.options.spiderLegPolylineOptions;
leg = new L.Polyline([this._latlng, newPos], legOptions);
map.addLayer(leg);
m._spiderLeg = leg;
}
this.setOpacity(0.3);
group.fire('spiderfied');
group._ignoreMove = false;
group.fire('spiderfied', {
cluster: this,
markers: childMarkers
});
},
_animationUnspiderfy: function () {
this._noanimationUnspiderfy();
}
} : {
//Animated versions here
SVG_ANIMATION: (function () {
return document.createElementNS('http://www.w3.org/2000/svg', 'animate').toString().indexOf('SVGAnimate') > -1;
}()),
});
//Animated versions here
L.MarkerCluster.include({
_animationSpiderfy: function (childMarkers, positions) {
var me = this,
group = this._group,
map = group._map,
fg = group._featureGroup,
thisLayerPos = map.latLngToLayerPoint(this._latlng),
i, m, leg, newPos;
thisLayerLatLng = this._latlng,
thisLayerPos = map.latLngToLayerPoint(thisLayerLatLng),
svg = L.Path.SVG,
legOptions = L.extend({}, this._group.options.spiderLegPolylineOptions), // Copy the options so that we can modify them for animation.
finalLegOpacity = legOptions.opacity,
i, m, leg, legPath, legLength, newPos;
//Add markers to map hidden at our center point
for (i = childMarkers.length - 1; i >= 0; i--) {
if (finalLegOpacity === undefined) {
finalLegOpacity = L.MarkerClusterGroup.prototype.options.spiderLegPolylineOptions.opacity;
}
if (svg) {
// If the initial opacity of the spider leg is not 0 then it appears before the animation starts.
legOptions.opacity = 0;
// Add the class for CSS transitions.
legOptions.className = (legOptions.className || '') + ' leaflet-cluster-spider-leg';
} else {
// Make sure we have a defined opacity.
legOptions.opacity = finalLegOpacity;
}
group._ignoreMove = true;
// Add markers and spider legs to map, hidden at our center point.
// Traverse in ascending order to make sure that inner circleMarkers are on top of further legs. Normal markers are re-ordered by newPosition.
// The reverse order trick no longer improves performance on modern browsers.
for (i = 0; i < childMarkers.length; i++) {
m = childMarkers[i];
//If it is a marker, add it now and we'll animate it out
if (m.setOpacity) {
m.setZIndexOffset(1000000); //Make these appear on top of EVERYTHING
m.clusterHide();
fg.addLayer(m);
newPos = map.layerPointToLatLng(positions[i]);
// Add the leg before the marker, so that in case the latter is a circleMarker, the leg is behind it.
leg = new L.Polyline([thisLayerLatLng, newPos], legOptions);
map.addLayer(leg);
m._spiderLeg = leg;
// Explanations: https://jakearchibald.com/2013/animated-line-drawing-svg/
// In our case the transition property is declared in the CSS file.
if (svg) {
legPath = leg._path;
legLength = legPath.getTotalLength() + 0.1; // Need a small extra length to avoid remaining dot in Firefox.
legPath.style.strokeDasharray = legLength; // Just 1 length is enough, it will be duplicated.
legPath.style.strokeDashoffset = legLength;
}
// If it is a marker, add it now and we'll animate it out
if (m.setZIndexOffset) {
m.setZIndexOffset(1000000); // Make normal markers appear on top of EVERYTHING
}
if (m.clusterHide) {
m.clusterHide();
}
// Vectors just get immediately added
fg.addLayer(m);
if (m._setPos) {
m._setPos(thisLayerPos);
} else {
//Vectors just get immediately added
fg.addLayer(m);
}
}
group._forceLayout();
group._animationStart();
var initialLegOpacity = L.Path.SVG ? 0 : 0.3,
xmlns = L.Path.SVG_NS;
// Reveal markers and spider legs.
for (i = childMarkers.length - 1; i >= 0; i--) {
newPos = map.layerPointToLatLng(positions[i]);
m = childMarkers[i];
@@ -195,84 +258,43 @@ L.MarkerCluster.include(!L.DomUtil.TRANSITION ? {
m._preSpiderfyLatlng = m._latlng;
m.setLatLng(newPos);
if (m.setOpacity) {
if (m.clusterShow) {
m.clusterShow();
}
//Add Legs.
var legOptions = this._group.options.spiderLegPolylineOptions;
if (legOptions.opacity === undefined) {
legOptions.opacity = initialLegOpacity;
}
leg = new L.Polyline([me._latlng, newPos], legOptions);
map.addLayer(leg);
m._spiderLeg = leg;
//Following animations don't work for canvas
if (!L.Path.SVG || !this.SVG_ANIMATION) {
continue;
}
//How this works:
//http://stackoverflow.com/questions/5924238/how-do-you-animate-an-svg-path-in-ios
//http://dev.opera.com/articles/view/advanced-svg-animation-techniques/
//Animate length
var length = leg._path.getTotalLength();
leg._path.setAttribute("stroke-dasharray", length + "," + length);
var anim = document.createElementNS(xmlns, "animate");
anim.setAttribute("attributeName", "stroke-dashoffset");
anim.setAttribute("begin", "indefinite");
anim.setAttribute("from", length);
anim.setAttribute("to", 0);
anim.setAttribute("dur", 0.25);
leg._path.appendChild(anim);
anim.beginElement();
//Animate opacity
anim = document.createElementNS(xmlns, "animate");
anim.setAttribute("attributeName", "stroke-opacity");
anim.setAttribute("attributeName", "stroke-opacity");
anim.setAttribute("begin", "indefinite");
anim.setAttribute("from", 0);
anim.setAttribute("to", 0.5);
anim.setAttribute("dur", 0.25);
leg._path.appendChild(anim);
anim.beginElement();
}
me.setOpacity(0.3);
//Set the opacity of the spiderLegs back to their correct value
// The animations above override this until they complete.
// If the initial opacity of the spiderlegs isn't 0 then they appear before the animation starts.
if (L.Path.SVG) {
this._group._forceLayout();
for (i = childMarkers.length - 1; i >= 0; i--) {
m = childMarkers[i]._spiderLeg;
m.options.opacity = 0.5;
m._path.setAttribute('stroke-opacity', 0.5);
// Animate leg (animation is actually delegated to CSS transition).
if (svg) {
leg = m._spiderLeg;
legPath = leg._path;
legPath.style.strokeDashoffset = 0;
//legPath.style.strokeOpacity = finalLegOpacity;
leg.setStyle({opacity: finalLegOpacity});
}
}
this.setOpacity(0.3);
group._ignoreMove = false;
setTimeout(function () {
group._animationEnd();
group.fire('spiderfied');
group.fire('spiderfied', {
cluster: me,
markers: childMarkers
});
}, 200);
},
_animationUnspiderfy: function (zoomDetails) {
var group = this._group,
var me = this,
group = this._group,
map = group._map,
fg = group._featureGroup,
thisLayerPos = zoomDetails ? map._latLngToNewLayerPoint(this._latlng, zoomDetails.zoom, zoomDetails.center) : map.latLngToLayerPoint(this._latlng),
childMarkers = this.getAllChildMarkers(),
svg = L.Path.SVG && this.SVG_ANIMATION,
m, i, a;
svg = L.Path.SVG,
m, i, leg, legPath, legLength, nonAnimatable;
group._ignoreMove = true;
group._animationStart();
//Make us visible and bring the child markers back in
@@ -280,39 +302,44 @@ L.MarkerCluster.include(!L.DomUtil.TRANSITION ? {
for (i = childMarkers.length - 1; i >= 0; i--) {
m = childMarkers[i];
//Marker was added to us after we were spidified
//Marker was added to us after we were spiderfied
if (!m._preSpiderfyLatlng) {
continue;
}
//Close any popup on the marker first, otherwise setting the location of the marker will make the map scroll
m.closePopup();
//Fix up the location to the real one
m.setLatLng(m._preSpiderfyLatlng);
delete m._preSpiderfyLatlng;
//Hack override the location to be our center
if (m.setOpacity) {
nonAnimatable = true;
if (m._setPos) {
m._setPos(thisLayerPos);
nonAnimatable = false;
}
if (m.clusterHide) {
m.clusterHide();
} else {
nonAnimatable = false;
}
if (nonAnimatable) {
fg.removeLayer(m);
}
//Animate the spider legs back in
// Animate the spider leg back in (animation is actually delegated to CSS transition).
if (svg) {
a = m._spiderLeg._path.childNodes[0];
a.setAttribute('to', a.getAttribute('from'));
a.setAttribute('from', 0);
a.beginElement();
a = m._spiderLeg._path.childNodes[1];
a.setAttribute('from', 0.5);
a.setAttribute('to', 0);
a.setAttribute('stroke-opacity', 0);
a.beginElement();
m._spiderLeg._path.setAttribute('stroke-opacity', 0);
leg = m._spiderLeg;
legPath = leg._path;
legLength = legPath.getTotalLength() + 0.1;
legPath.style.strokeDashoffset = legLength;
leg.setStyle({opacity: 0});
}
}
group._ignoreMove = false;
setTimeout(function () {
//If we have only <= one child left then that marker will be shown on the map so don't remove it!
var stillThereChildCount = 0;
@@ -331,9 +358,10 @@ L.MarkerCluster.include(!L.DomUtil.TRANSITION ? {
continue;
}
if (m.setOpacity) {
if (m.clusterShow) {
m.clusterShow();
}
if (m.setZIndexOffset) {
m.setZIndexOffset(0);
}
@@ -345,6 +373,10 @@ L.MarkerCluster.include(!L.DomUtil.TRANSITION ? {
delete m._spiderLeg;
}
group._animationEnd();
group.fire('unspiderfied', {
cluster: me,
markers: childMarkers
});
}, 200);
}
});
@@ -354,6 +386,10 @@ L.MarkerClusterGroup.include({
//The MarkerCluster currently spiderfied (if any)
_spiderfied: null,
unspiderfy: function () {
this._unspiderfy.apply(this, arguments);
},
_spiderfierOnAdd: function () {
this._map.on('click', this._unspiderfyWrapper, this);
@@ -363,8 +399,8 @@ L.MarkerClusterGroup.include({
//Browsers without zoomAnimation or a big zoom don't fire zoomstart
this._map.on('zoomend', this._noanimationUnspiderfy, this);
if (L.Path.SVG && !L.Browser.touch) {
this._map._initPathRoot();
if (!L.Browser.touch) {
this._map.getRenderer(this);
//Needs to happen in the pageload, not after, or animations don't work in webkit
// http://stackoverflow.com/questions/8455200/svg-animate-with-dynamically-added-elements
//Disable on touch browsers as the animation messes up on a touch zoom and isn't very noticable
@@ -375,11 +411,13 @@ L.MarkerClusterGroup.include({
this._map.off('click', this._unspiderfyWrapper, this);
this._map.off('zoomstart', this._unspiderfyZoomStart, this);
this._map.off('zoomanim', this._unspiderfyZoomAnim, this);
this._map.off('zoomend', this._noanimationUnspiderfy, this);
this._unspiderfy(); //Ensure that markers are back where they should be
//Ensure that markers are back where they should be
// Use no animation to avoid a sticky leaflet-cluster-anim class on mapPane
this._noanimationUnspiderfy();
},
//On zoom start we add a zoomanim handler so that we are guaranteed to be last (after markers are animated)
//This means we can define the animation they do rather than Markers doing an animation to their actual location
_unspiderfyZoomStart: function () {
@@ -389,6 +427,7 @@ L.MarkerClusterGroup.include({
this._map.on('zoomanim', this._unspiderfyZoomAnim, this);
},
_unspiderfyZoomAnim: function (zoomDetails) {
//Wait until the first zoomanim after the user has finished touch-zooming before running the animation
if (L.DomUtil.hasClass(this._map._mapPane, 'leaflet-touching')) {
@@ -399,7 +438,6 @@ L.MarkerClusterGroup.include({
this._unspiderfy(zoomDetails);
},
_unspiderfyWrapper: function () {
/// <summary>_unspiderfy but passes no arguments</summary>
this._unspiderfy();
@@ -422,9 +460,13 @@ L.MarkerClusterGroup.include({
if (layer._spiderLeg) {
this._featureGroup.removeLayer(layer);
layer.setOpacity(1);
//Position will be fixed up immediately in _animationUnspiderfy
layer.setZIndexOffset(0);
if (layer.clusterShow) {
layer.clusterShow();
}
//Position will be fixed up immediately in _animationUnspiderfy
if (layer.setZIndexOffset) {
layer.setZIndexOffset(0);
}
this._map.removeLayer(layer._spiderLeg);
delete layer._spiderLeg;
+99 -71
View File
@@ -1,8 +1,8 @@
L.MarkerCluster = L.Marker.extend({
initialize: function (group, zoom, a, b) {
L.Marker.prototype.initialize.call(this, a ? (a._cLatLng || a.getLatLng()) : new L.LatLng(0, 0), { icon: this });
L.Marker.prototype.initialize.call(this, a ? (a._cLatLng || a.getLatLng()) : new L.LatLng(0, 0),
{ icon: this, pane: group.options.clusterPane });
this._group = group;
this._zoom = zoom;
@@ -11,6 +11,7 @@ L.MarkerCluster = L.Marker.extend({
this._childClusters = [];
this._childCount = 0;
this._iconNeedsUpdate = true;
this._boundsNeedUpdate = true;
this._bounds = new L.LatLngBounds();
@@ -43,7 +44,7 @@ L.MarkerCluster = L.Marker.extend({
},
//Zoom to the minimum of showing all of the child markers, or the extents of this cluster
zoomToBounds: function () {
zoomToBounds: function (fitBoundsOptions) {
var childClusters = this._childClusters.slice(),
map = this._group._map,
boundsZoom = map.getBoundsZoom(this._bounds),
@@ -66,7 +67,7 @@ L.MarkerCluster = L.Marker.extend({
} else if (boundsZoom <= mapZoom) { //If fitBounds wouldn't zoom us down, zoom us down instead
this._group._map.setView(this._latlng, mapZoom + 1);
} else {
this._group._map.fitBounds(this._bounds);
this._group._map.fitBounds(this._bounds, fitBoundsOptions);
}
},
@@ -99,7 +100,9 @@ L.MarkerCluster = L.Marker.extend({
_addChild: function (new1, isNotificationFromChild) {
this._iconNeedsUpdate = true;
this._expandBounds(new1);
this._boundsNeedUpdate = true;
this._setClusterCenter(new1);
if (new1 instanceof L.MarkerCluster) {
if (!isNotificationFromChild) {
@@ -119,34 +122,85 @@ L.MarkerCluster = L.Marker.extend({
}
},
//Expand our bounds and tell our parent to
_expandBounds: function (marker) {
var addedCount,
addedLatLng = marker._wLatLng || marker._latlng;
if (marker instanceof L.MarkerCluster) {
this._bounds.extend(marker._bounds);
addedCount = marker._childCount;
} else {
this._bounds.extend(addedLatLng);
addedCount = 1;
}
/**
* Makes sure the cluster center is set. If not, uses the child center if it is a cluster, or the marker position.
* @param child L.MarkerCluster|L.Marker that will be used as cluster center if not defined yet.
* @private
*/
_setClusterCenter: function (child) {
if (!this._cLatLng) {
// when clustering, take position of the first point as the cluster center
this._cLatLng = marker._cLatLng || addedLatLng;
this._cLatLng = child._cLatLng || child._latlng;
}
},
/**
* Assigns impossible bounding values so that the next extend entirely determines the new bounds.
* This method avoids having to trash the previous L.LatLngBounds object and to create a new one, which is much slower for this class.
* As long as the bounds are not extended, most other methods would probably fail, as they would with bounds initialized but not extended.
* @private
*/
_resetBounds: function () {
var bounds = this._bounds;
if (bounds._southWest) {
bounds._southWest.lat = Infinity;
bounds._southWest.lng = Infinity;
}
if (bounds._northEast) {
bounds._northEast.lat = -Infinity;
bounds._northEast.lng = -Infinity;
}
},
_recalculateBounds: function () {
var markers = this._markers,
childClusters = this._childClusters,
latSum = 0,
lngSum = 0,
totalCount = this._childCount,
i, child, childLatLng, childCount;
// Case where all markers are removed from the map and we are left with just an empty _topClusterLevel.
if (totalCount === 0) {
return;
}
// when showing clusters, take weighted average of all points as cluster center
var totalCount = this._childCount + addedCount;
// Reset rather than creating a new object, for performance.
this._resetBounds();
//Calculate weighted latlng for display
if (!this._wLatLng) {
this._latlng = this._wLatLng = new L.LatLng(addedLatLng.lat, addedLatLng.lng);
} else {
this._wLatLng.lat = (addedLatLng.lat * addedCount + this._wLatLng.lat * this._childCount) / totalCount;
this._wLatLng.lng = (addedLatLng.lng * addedCount + this._wLatLng.lng * this._childCount) / totalCount;
// Child markers.
for (i = 0; i < markers.length; i++) {
childLatLng = markers[i]._latlng;
this._bounds.extend(childLatLng);
latSum += childLatLng.lat;
lngSum += childLatLng.lng;
}
// Child clusters.
for (i = 0; i < childClusters.length; i++) {
child = childClusters[i];
// Re-compute child bounds and weighted position first if necessary.
if (child._boundsNeedUpdate) {
child._recalculateBounds();
}
this._bounds.extend(child._bounds);
childLatLng = child._wLatLng;
childCount = child._childCount;
latSum += childLatLng.lat * childCount;
lngSum += childLatLng.lng * childCount;
}
this._latlng = this._wLatLng = new L.LatLng(latSum / totalCount, lngSum / totalCount);
// Reset dirty flag.
this._boundsNeedUpdate = false;
},
//Set our markers position as given and add it to the map
@@ -159,7 +213,7 @@ L.MarkerCluster = L.Marker.extend({
},
_recursivelyAnimateChildrenIn: function (bounds, center, maxZoom) {
this._recursively(bounds, 0, maxZoom - 1,
this._recursively(bounds, this._group._map.getMinZoom(), maxZoom - 1,
function (c) {
var markers = c._markers,
i, m;
@@ -187,8 +241,8 @@ L.MarkerCluster = L.Marker.extend({
);
},
_recursivelyAnimateChildrenInAndAddSelfToMap: function (bounds, previousZoomLevel, newZoomLevel) {
this._recursively(bounds, newZoomLevel, 0,
_recursivelyAnimateChildrenInAndAddSelfToMap: function (bounds, mapMinZoom, previousZoomLevel, newZoomLevel) {
this._recursively(bounds, newZoomLevel, mapMinZoom,
function (c) {
c._recursivelyAnimateChildrenIn(bounds, c._group._map.latLngToLayerPoint(c.getLatLng()).round(), previousZoomLevel);
@@ -196,7 +250,7 @@ L.MarkerCluster = L.Marker.extend({
//As a hack we only do a animation free zoom on a single level zoom, if someone does multiple levels then we always animate
if (c._isSingleParent() && previousZoomLevel - 1 === newZoomLevel) {
c.clusterShow();
c._recursivelyRemoveChildrenFromMap(bounds, previousZoomLevel); //Immediately remove our children as we are replacing them. TODO previousBounds not bounds
c._recursivelyRemoveChildrenFromMap(bounds, mapMinZoom, previousZoomLevel); //Immediately remove our children as we are replacing them. TODO previousBounds not bounds
} else {
c.clusterHide();
}
@@ -207,13 +261,13 @@ L.MarkerCluster = L.Marker.extend({
},
_recursivelyBecomeVisible: function (bounds, zoomLevel) {
this._recursively(bounds, 0, zoomLevel, null, function (c) {
this._recursively(bounds, this._group._map.getMinZoom(), zoomLevel, null, function (c) {
c.clusterShow();
});
},
_recursivelyAddChildrenToMap: function (startPos, zoomLevel, bounds) {
this._recursively(bounds, -1, zoomLevel,
this._recursively(bounds, this._group._map.getMinZoom() - 1, zoomLevel,
function (c) {
if (zoomLevel === c._zoom) {
return;
@@ -275,9 +329,9 @@ L.MarkerCluster = L.Marker.extend({
},
//exceptBounds: If set, don't remove any markers/clusters in it
_recursivelyRemoveChildrenFromMap: function (previousBounds, zoomLevel, exceptBounds) {
_recursivelyRemoveChildrenFromMap: function (previousBounds, mapMinZoom, zoomLevel, exceptBounds) {
var m, i;
this._recursively(previousBounds, -1, zoomLevel - 1,
this._recursively(previousBounds, mapMinZoom - 1, zoomLevel - 1,
function (c) {
//Remove markers at every level
for (i = c._markers.length - 1; i >= 0; i--) {
@@ -316,51 +370,25 @@ L.MarkerCluster = L.Marker.extend({
zoom = this._zoom,
i, c;
if (zoomLevelToStart > zoom) { //Still going down to required depth, just recurse to child clusters
if (zoomLevelToStart <= zoom) {
if (runAtEveryLevel) {
runAtEveryLevel(this);
}
if (runAtBottomLevel && zoom === zoomLevelToStop) {
runAtBottomLevel(this);
}
}
if (zoom < zoomLevelToStart || zoom < zoomLevelToStop) {
for (i = childClusters.length - 1; i >= 0; i--) {
c = childClusters[i];
if (boundsToApplyTo.intersects(c._bounds)) {
c._recursively(boundsToApplyTo, zoomLevelToStart, zoomLevelToStop, runAtEveryLevel, runAtBottomLevel);
}
}
} else { //In required depth
if (runAtEveryLevel) {
runAtEveryLevel(this);
}
if (runAtBottomLevel && this._zoom === zoomLevelToStop) {
runAtBottomLevel(this);
}
//TODO: This loop is almost the same as above
if (zoomLevelToStop > zoom) {
for (i = childClusters.length - 1; i >= 0; i--) {
c = childClusters[i];
if (boundsToApplyTo.intersects(c._bounds)) {
c._recursively(boundsToApplyTo, zoomLevelToStart, zoomLevelToStop, runAtEveryLevel, runAtBottomLevel);
}
}
}
}
},
_recalculateBounds: function () {
var markers = this._markers,
childClusters = this._childClusters,
i;
this._bounds = new L.LatLngBounds();
delete this._wLatLng;
for (i = markers.length - 1; i >= 0; i--) {
this._expandBounds(markers[i]);
}
for (i = childClusters.length - 1; i >= 0; i--) {
this._expandBounds(childClusters[i]);
}
},
//Returns true if we are the parent of only one cluster and that cluster is the same as us
_isSingleParent: function () {
//Don't need to check this._markers as the rest won't work if there are any
@@ -0,0 +1,110 @@
/**
* Adds 1 public method to MCG and 1 to L.Marker to facilitate changing
* markers' icon options and refreshing their icon and their parent clusters
* accordingly (case where their iconCreateFunction uses data of childMarkers
* to make up the cluster icon).
*/
L.MarkerClusterGroup.include({
/**
* Updates the icon of all clusters which are parents of the given marker(s).
* In singleMarkerMode, also updates the given marker(s) icon.
* @param layers L.MarkerClusterGroup|L.LayerGroup|Array(L.Marker)|Map(L.Marker)|
* L.MarkerCluster|L.Marker (optional) list of markers (or single marker) whose parent
* clusters need to be updated. If not provided, retrieves all child markers of this.
* @returns {L.MarkerClusterGroup}
*/
refreshClusters: function (layers) {
if (!layers) {
layers = this._topClusterLevel.getAllChildMarkers();
} else if (layers instanceof L.MarkerClusterGroup) {
layers = layers._topClusterLevel.getAllChildMarkers();
} else if (layers instanceof L.LayerGroup) {
layers = layers._layers;
} else if (layers instanceof L.MarkerCluster) {
layers = layers.getAllChildMarkers();
} else if (layers instanceof L.Marker) {
layers = [layers];
} // else: must be an Array(L.Marker)|Map(L.Marker)
this._flagParentsIconsNeedUpdate(layers);
this._refreshClustersIcons();
// In case of singleMarkerMode, also re-draw the markers.
if (this.options.singleMarkerMode) {
this._refreshSingleMarkerModeMarkers(layers);
}
return this;
},
/**
* Simply flags all parent clusters of the given markers as having a "dirty" icon.
* @param layers Array(L.Marker)|Map(L.Marker) list of markers.
* @private
*/
_flagParentsIconsNeedUpdate: function (layers) {
var id, parent;
// Assumes layers is an Array or an Object whose prototype is non-enumerable.
for (id in layers) {
// Flag parent clusters' icon as "dirty", all the way up.
// Dumb process that flags multiple times upper parents, but still
// much more efficient than trying to be smart and make short lists,
// at least in the case of a hierarchy following a power law:
// http://jsperf.com/flag-nodes-in-power-hierarchy/2
parent = layers[id].__parent;
while (parent) {
parent._iconNeedsUpdate = true;
parent = parent.__parent;
}
}
},
/**
* Re-draws the icon of the supplied markers.
* To be used in singleMarkerMode only.
* @param layers Array(L.Marker)|Map(L.Marker) list of markers.
* @private
*/
_refreshSingleMarkerModeMarkers: function (layers) {
var id, layer;
for (id in layers) {
layer = layers[id];
// Make sure we do not override markers that do not belong to THIS group.
if (this.hasLayer(layer)) {
// Need to re-create the icon first, then re-draw the marker.
layer.setIcon(this._overrideMarkerIcon(layer));
}
}
}
});
L.Marker.include({
/**
* Updates the given options in the marker's icon and refreshes the marker.
* @param options map object of icon options.
* @param directlyRefreshClusters boolean (optional) true to trigger
* MCG.refreshClustersOf() right away with this single marker.
* @returns {L.Marker}
*/
refreshIconOptions: function (options, directlyRefreshClusters) {
var icon = this.options.icon;
L.setOptions(icon, options);
this.setIcon(icon);
// Shortcut to refresh the associated MCG clusters right away.
// To be used when refreshing a single marker.
// Otherwise, better use MCG.refreshClusters() once at the end with
// the list of modified markers.
if (directlyRefreshClusters && this.__parent) {
this.__parent._group.refreshClusters(this);
}
return this;
}
});
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1,5 +1,5 @@
/*
Leaflet.markercluster, Provides Beautiful Animated Marker Clustering functionality for Leaflet, a JS library for interactive maps.
https://github.com/Leaflet/Leaflet.markercluster
(c) 2012-2013, Dave Leaver, smartrak
(c) 2012-2017, Dave Leaver
*/