├── .gitignore ├── LICENSE.txt ├── Makefile ├── README.md ├── public_html ├── bundle │ └── .gitkeep ├── data │ └── .gitkeep ├── groupxiv.css ├── groupxiv.js ├── index.html ├── vendor │ ├── leaflet.draw │ │ ├── images │ │ │ ├── layers-2x.png │ │ │ ├── layers.png │ │ │ ├── marker-icon-2x.png │ │ │ ├── marker-icon.png │ │ │ ├── marker-shadow.png │ │ │ ├── spritesheet-2x.png │ │ │ ├── spritesheet.png │ │ │ └── spritesheet.svg │ │ ├── leaflet.draw.css │ │ └── leaflet.draw.js │ ├── leaflet.fullscreen │ │ ├── Control.FullScreen.css │ │ ├── Control.FullScreen.js │ │ └── images │ │ │ ├── icon-fullscreen-2x.png │ │ │ └── icon-fullscreen.png │ ├── leaflet.loading │ │ ├── Control.Loading.css │ │ └── Control.Loading.js │ ├── leaflet.nanomeasure │ │ ├── Control.Nanomeasure.css │ │ ├── Control.Nanomeasure.js │ │ └── images │ │ │ ├── measure-area-2x.png │ │ │ ├── measure-coordinates-2x.png │ │ │ └── measure-distance-2x.png │ ├── leaflet.nanoscale │ │ └── Control.Nanoscale.js │ ├── leaflet.tilelayer.fallback │ │ └── leaflet.tilelayer.fallback.js │ └── leaflet │ │ ├── images │ │ ├── layers-2x.png │ │ ├── layers.png │ │ ├── marker-icon-2x.png │ │ ├── marker-icon.png │ │ └── marker-shadow.png │ │ ├── leaflet.css │ │ └── leaflet.js └── viewer.js └── tile_cutter ├── __init__.py └── __main__.py /.gitignore: -------------------------------------------------------------------------------- 1 | /public_html/bundle/* 2 | /public_html/data/* 3 | *.py[co] 4 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 whitequark@whitequark.org 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | JAVASCRIPTS = \ 2 | vendor/leaflet/leaflet.js \ 3 | vendor/leaflet.tilelayer.fallback/leaflet.tilelayer.fallback.js \ 4 | vendor/leaflet.draw/leaflet.draw.js \ 5 | vendor/leaflet.fullscreen/Control.FullScreen.js \ 6 | vendor/leaflet.loading/Control.Loading.js \ 7 | vendor/leaflet.nanoscale/Control.Nanoscale.js \ 8 | vendor/leaflet.nanomeasure/Control.Nanomeasure.js \ 9 | groupxiv.js \ 10 | viewer.js 11 | 12 | STYLESHEETS = \ 13 | vendor/leaflet/leaflet.css \ 14 | vendor/leaflet.draw/leaflet.draw.css \ 15 | vendor/leaflet.fullscreen/Control.FullScreen.css \ 16 | vendor/leaflet.loading/Control.Loading.css \ 17 | vendor/leaflet.nanomeasure/Control.Nanomeasure.css \ 18 | groupxiv.css 19 | 20 | IMAGES = \ 21 | vendor/leaflet/images \ 22 | vendor/leaflet.fullscreen/images \ 23 | vendor/leaflet.draw/images \ 24 | vendor/leaflet.nanomeasure/images 25 | 26 | .PHONY: bundle 27 | bundle: 28 | cat $(addprefix public_html/,$(JAVASCRIPTS)) >public_html/bundle/groupxiv.js 29 | cat $(addprefix public_html/,$(STYLESHEETS)) >public_html/bundle/groupxiv.css 30 | cp -r $(addprefix public_html/,$(IMAGES)) public_html/bundle/ 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | GroupXIV 2 | ======== 3 | 4 | GroupXIV is a microphotography viewer based on [Leaflet](http://leafletjs.com). It allows to cut an arbitrarily large image into tiles, conveniently display them on a desktop or mobile browser, create persistent URLs, and measure distances and areas. 5 | 6 | For example, see [Atmel ATmega8](http://groupxiv.whitequark.org/#url=data/atmega8.png.json), [Atmel ATtiny24V](http://groupxiv.whitequark.org/#url=data/attiny24v.png.json), [Epson S1D15719](http://groupxiv.whitequark.org/#url=data/S1D15719.png.json). 7 | 8 | Requirements 9 | ------------ 10 | 11 | The tile cutter depends on [Python 3](https://python.org/) and [wand](http://docs.wand-py.org/en/). 12 | 13 | The viewer has no server-side code, so it will work with any webserver. 14 | 15 | Deploying 16 | --------- 17 | 18 | First, generate the JavaScript and CSS bundle: 19 | 20 | make bundle 21 | 22 | Serve the `public_html` folder from any convenient URL. 23 | 24 | For example, you could use Python's builtin HTTP server: 25 | 26 | cd public_html && python3 -m http.server 8000 27 | 28 | Adding tiles 29 | ------------ 30 | 31 | 1. Assuming an image called `image.png`, put `image.png` in `public_html/data`. 32 | 2. Run `python -m tile_cutter public_html/data/image.png` from the repository root. This will create: 33 | * `public_html/data/image.png-tiles`, containing the sliced image; 34 | * `public_html/data/image.png.json`, containing the image metadata. 35 | 3. Change the following metadata fields: 36 | * `name` to describe the source of the image, e.g. `"Atmel ATmega8"`; 37 | * `scale` to contain the ratio of pixels to nanometers, e.g. `540` for 540 nanometers per pixel. 38 | 4. Assuming `public_html` is served from `https://groupxiv/`, navigate to `https://groupxiv/#url=data/image.png.json`. 39 | 40 | The original `image.png` is no longer necessary, however it is recommended to keep it for anyone who would like to download the source of the tileset. 41 | 42 | ImageMagick policy limits 43 | ------------------------- 44 | 45 | You may find that large images fail to convert, giving one of these messages: 46 | 47 | ``` 48 | wand.exceptions.ImageError: width or height exceeds limit `' @ error/cache.c/OpenPixelCache/3909 49 | wand.exceptions.CacheError: cache resources exhausted `' @ error/cache.c/OpenPixelCache/4095 50 | wand.exceptions.WandError: wand contains no images `MagickWand-1' @ error/magick-image.c/MagickGetImageWidth/6336 51 | ``` 52 | 53 | These errors are caused by a security policy which has been introduced by ImageMagick to deal with buffer overflow attacks. 54 | 55 | To fix this, edit `/etc/ImageMagick-6/policy.xml`: 56 | 57 | * `width or height exceeds limit`: increase `domain="resource" name="width"` and `name="height"` to be greater than the input image. 58 | * `wand contains no images`: increase `domain="resource" name="width"` and `name="height"` to be greater than the `tiled size` groupXIV prints out. 59 | * `cache resources exhausted`: increase `domain="resource" name="area"` (maximum image cache area in pixels) and `domain="resource" name="disk"` (maximum disk space to use for caching large images). 60 | 61 | The following settings seem to work for images up to about 30,000 pixels square, which produced a 32768-pixel tiled square: 62 | 63 | * `width` and `height`: `70KP` (70,000 pixels) 64 | * `area`: `128GP` (128 gigapixels) 65 | * `disk`: `30GiB` (30 gigabytes) 66 | 67 | The command `identify -list resource` can be used to display the current resource limits. 68 | 69 | Future improvements 70 | ------------------- 71 | 72 | As soon as I have a setup for capturing multi-layer imagery, I plan to add multi-layer support. The JSON metadata format already supports it somewhat. 73 | 74 | See also 75 | -------- 76 | 77 | GroupXIV uses two Leaflet controls developed specifically for it: [Leaflet.Nanoscale](https://github.com/whitequark/Leaflet.Nanoscale) and [Leaflet.Nanomeasure](https://github.com/whitequark/Leaflet.Nanomeasure). 78 | 79 | Bonus: microphotography tips 80 | ---------------------------- 81 | 82 | * Image stitching software can mitigate a reasonable amount of out-of-focus pixels; even 30% usually produces tolerable results as long as every area is in focus at least once. 83 | * If your imaging setup consistently produces out-of-focus pixels in the same regions, it's best to cut them out, e.g. using ImageMagick: `for i in raw*.png; do convert $i cropped-$i -crop WxH+X+Y`. 84 | * Image stitching software can mitigate a substantial difference in exposure, but it is instead recommended to keep exposure constant during capture. A good idea is to find an area where a low-reflectivity area, such as many thin metal interconnect traces, is immediately adjacent to a high-reflectivity area, such as a metal polygon, and adjust exposure so that neither is under- or overexposed. 85 | 86 | Bonus: image stitching with Hugin 87 | --------------------------------- 88 | 89 | [Hugin](http://hugin.sourceforge.net/) is a very powerful application for stitching images, however its intended domain is panoramas and the UI does not make it easy to stitch flat tiles. Here is a step-by-step guide: 90 | 91 | 1. Select _Interface_ → _Expert_. 92 | 2. On _Photos_ tab under _Lens type_, select _Add images..._. When prompted for field of view, enter 10; this value is not important. 93 | 3. On _Photos_ tab under _Feature Matching_, _Settings:_, select "Cpfind (multirow/stacked)". Click _Create control points_. This can take a few minutes to a few hours. 94 | 4. On _Photos_ tab under _Optimize_, _Geometric:_, select "Custom parameters". Do not click _Calculate_ yet. 95 | 5. On _Optimizer_ tab (tab appears after step 3) under _Image Orientation_, right-click on every column except _TrX_ and _TrY_ and click _Unselect all_. Right-click on _TrX_ and _TrY_ and click _Select all_. Make sure only values, and all values, in _TrX_ and _TrY_ columns are bold. (If your images are not level, add _Roll_ to that set.) 96 | 6. On _Optimizer_ tab (tab appears after step 3) under _Lens Parameters_, right-click on every column and click _Unselect all_. 97 | 7. On _Optimizer_ tab, click _Reset_, select every checkbox and click _OK_. 98 | 8. On _Optimizer_ tab, click _Optimize now!_. This can take a few minutes to few hours. 99 | 9. On _Photos_ tab, select all photos, right-click, select _Control points_, _Clean control points_. This can take a few minutes. 100 | 10. On _Optimizer_ tab, click _Optimize now!_ (again). This can take a few minutes to few hours, but quicker than the first one. 101 | 11. Select _View_ → _Fast Preview window_. This will open a new window. 102 | 12. In preview window, under _Projection_ tab, left list box, select "Normal (rectilinear)". 103 | 13. In preview window, use the sliders to the bottom and the right to fit the image in the viewing area; under _Move/Drag_ tab, _Drag mode:_, select "mosaic", then draw the image at the center. It allows you to estimate whether the fit is good. A good fit is seamless and all straight lines on the sample should appear completely straight in Hugin. 104 | 14. In preview window, select the _Crop_ tab, then move the areas that are highlighted when you move the cursor near the edges of the viewing area so that only the sample is inside the white rectangle. 105 | 15. In main window, under _Stitcher_ tab, under _Canvas size:_, click _Calculate optimal size_; under _Panorama Outputs:_ select "Exposure corrected, low dynamic range"; under _Panorama Outputs:_, _Format:_ select "PNG", under _Processing_, _Blender:_, click _Options_, enter `--fine-mask`, click _OK_. 106 | 16. In main window, under _Stitcher_ tab click _Stitch!_. This will first open a save dialog for the Hugin project, then it will open another save dialog for the panorama output as well as intermediates (which will be temporarily placed in the same location as the panorama output), then it will open a PTBatcherGUI window. PTBatcherGUI could complain about assertion failures; ignore that. 107 | 17. PTBatcherGUI will automatically process all files in a few dozens of minutes to a few hours. Done! 108 | 109 | Key points: 110 | 111 | * Multirow CPfind is an optimal control point search method for images that are taken sequentially in multiple rows, taking less comparisons than a generic pairwise method. 112 | * Microphotography has practically no optical distortion to speak of and the images are usually perfectly, or near-perfectly level. Thus the only parameters Hugin should try to adjust is the X and Y translation. If it tries to adjust others, it will certainly overoptimize, especially on highly similar images such as large chunks of metal interconnect. 113 | * For the same reason, Hugin should assume a rectilinear lens, i.e. a lens that makes straight lines appear straight on the pictures. 114 | * `--fine-mask` is a workaround for a rare but annoying enblend bug. 115 | * Trying to move images around in any way except with a rectilinear lens and mosaic mode will change positional parameters of the images and it'll be necessary to reset these and re-optimize. 116 | 117 | Alternatively, replace the steps 1-13 with the following script: 118 | 119 | ``` sh 120 | #!/bin/sh 121 | set -e 122 | 123 | PROJECT="$1" 124 | IMAGES="$2" 125 | 126 | if [ -z "${IMAGES}" ]; then 127 | echo >&2 "Usage: $0 ''" 128 | echo >&2 " e.g.: $0 attiny2313 'raw*.png'" 129 | exit 1 130 | fi 131 | 132 | pto_gen -o ${PROJECT}_1.pto ${IMAGES} 133 | cpfind -o ${PROJECT}_2.pto ${PROJECT}_1.pto --multirow 134 | pto_var -o ${PROJECT}_3.pto ${PROJECT}_2.pto --opt TrX,TrY 135 | autooptimiser -o ${PROJECT}_4.pto ${PROJECT}_3.pto -n 136 | cpclean -o ${PROJECT}_5.pto ${PROJECT}_4.pto 137 | autooptimiser -o ${PROJECT}_6.pto ${PROJECT}_5.pto -n 138 | sed >${PROJECT}_7.pto <${PROJECT}_6.pto -e 's,^p .*$,p f0 w3000 h1500 v179 E0 R0 n"TIFF_m c:LZW r:CROP",' 139 | ``` 140 | 141 | From that you still need to proceed via hugin's GUI. 142 | 143 | License 144 | ------- 145 | 146 | [MIT license](LICENSE.txt) 147 | -------------------------------------------------------------------------------- /public_html/bundle/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitequark/groupXIV/d2140be3acbdd20286f9eddc5ec10b8db67b817a/public_html/bundle/.gitkeep -------------------------------------------------------------------------------- /public_html/data/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitequark/groupXIV/d2140be3acbdd20286f9eddc5ec10b8db67b817a/public_html/data/.gitkeep -------------------------------------------------------------------------------- /public_html/groupxiv.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | } 4 | #viewer { 5 | position: absolute; 6 | width: 100%; 7 | height: 100%; 8 | background-color: #202020; 9 | } 10 | #viewer > .error { 11 | padding-top: 1em; 12 | text-align: center; 13 | color: #ffffff; 14 | } 15 | -------------------------------------------------------------------------------- /public_html/groupxiv.js: -------------------------------------------------------------------------------- 1 | /* 2 | Options: 3 | viewport: DOM id of the viewport 4 | scale: image scale (nm/px) 5 | layers: array of: 6 | URL: URL of the original image 7 | width: original image width 8 | height: original image height 9 | tileSize: tile dimension 10 | imageSize: smallest square image size that fits all tiles at maximum zoom 11 | minZoom: minimum zoom level (default: 1) 12 | maxZoom: maximum zoom level (default: ceil(log2(imageSize/tileSize)) + 1) 13 | tilesAlignedTopLeft: 14 | if true, clip margins for tiles starting at top left and ending at (width, height) 15 | if false, clip margins for tiles centered to (imageSize, imageSize) rectangle 16 | tileBuffer: amount of tiles around the viewport to retain when panning 17 | */ 18 | function GroupXIV(options) { 19 | var viewport = options.viewport, 20 | scale = options.scale, 21 | layers = options.layers, 22 | tileBuffer = options.tileBuffer; 23 | 24 | var maxImageSize = 0, maxWidth = 0, maxHeight = 0, minZoom = 1, maxZoom = 1; 25 | layers.forEach(function(layer) { 26 | if(layer.imageSize > maxImageSize) 27 | maxImageSize = layer.imageSize; 28 | if(layer.width > maxWidth) 29 | maxWidth = layer.width; 30 | if(layer.height > maxHeight) 31 | maxHeight = layer.height; 32 | 33 | var layerMaxDim = Math.max(layer.width, layer.height); 34 | var layerMinZoom = layer.minZoom, layerMaxZoom = layer.maxZoom; 35 | if(layerMinZoom === undefined) 36 | layerMinZoom = 1; 37 | if(layerMaxZoom === undefined) 38 | layerMaxZoom = Math.ceil(Math.log2(layer.imageSize / layer.tileSize)); 39 | 40 | if(layerMinZoom < minZoom) 41 | minZoom = layerMinZoom; 42 | if(layerMaxZoom > maxZoom) 43 | maxZoom = layerMaxZoom; 44 | }); 45 | 46 | var map = L.map(options.viewport, { 47 | minZoom: minZoom, 48 | maxZoom: maxZoom + 1, 49 | crs: L.CRS.Simple, 50 | wheelPxPerZoomLevel: 120, 51 | }); 52 | 53 | var bounds; 54 | if(options.tilesAlignedTopLeft) { 55 | bounds = new L.LatLngBounds( 56 | map.unproject([0, 0], maxZoom), 57 | map.unproject([maxWidth, maxHeight], maxZoom)); 58 | } else { 59 | var marginX = (maxImageSize - maxWidth) / 2, 60 | marginY = (maxImageSize - maxHeight) / 2; 61 | bounds = new L.LatLngBounds( 62 | map.unproject([maxImageSize - marginX, marginY], maxZoom), 63 | map.unproject([marginX, maxImageSize - marginY], maxZoom)); 64 | } 65 | map.fitBounds(bounds); 66 | map.setMaxBounds(bounds.pad(0.5)); 67 | 68 | var hasBaseLayer = false, baseLayers = {}, overlays = {}; 69 | layers.forEach(function(layer) { 70 | var layerMaxZoom = layer.maxZoom; 71 | if(layerMaxZoom === undefined) 72 | layerMaxZoom = Math.ceil(Math.log2(layer.imageSize / layer.tileSize)); 73 | 74 | var attribution = "Layer "; 75 | if(layer.name) { 76 | attribution += layer.name + " (" + layer.URL + ")"; 77 | } else { 78 | attribution += layer.URL; 79 | } 80 | if(layer.copyright) { 81 | attribution += " \u00a9 " + layer.copyright; 82 | } 83 | 84 | var tileExt = ".png"; 85 | if(layer.tileExt) { 86 | tileExt = layer.tileExt; 87 | } 88 | 89 | var tileLayer = L.tileLayer.fallback(layer.URL + "-tiles/{z}/{x}/{y}" + tileExt, { 90 | maxNativeZoom: layerMaxZoom, 91 | bounds: bounds, 92 | tileSize: layer.tileSize, 93 | continuousWorld: true, 94 | detectRetina: true, 95 | attribution: attribution, 96 | keepBuffer: tileBuffer, 97 | }); 98 | if(!hasBaseLayer) { 99 | hasBaseLayer = true; 100 | tileLayer.addTo(map); 101 | } 102 | baseLayers[layer.name] = tileLayer; 103 | }); 104 | 105 | L.control.layers(baseLayers, overlays).addTo(map); 106 | 107 | if(scale !== undefined) { 108 | L.control.nanoscale({ 109 | nanometersPerPixel: scale, 110 | ratioAtZoom: maxZoom, 111 | }).addTo(map); 112 | 113 | L.control.nanomeasure({ 114 | nanometersPerPixel: scale, 115 | ratioAtZoom: maxZoom, 116 | }).addTo(map); 117 | } 118 | 119 | L.control.fullscreen({ 120 | forceSeparateButton: true, 121 | }).addTo(map); 122 | 123 | map.on('enterFullscreen', function(){ 124 | document.getElementById('viewer').style.position = 'relative'; 125 | }); 126 | 127 | map.on('exitFullscreen', function(){ 128 | document.getElementById('viewer').style.position = 'absolute'; 129 | }); 130 | 131 | L.Control.loading({ 132 | separate: true, 133 | }).addTo(map); 134 | 135 | return map; 136 | } 137 | -------------------------------------------------------------------------------- /public_html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Loading... 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /public_html/vendor/leaflet.draw/images/layers-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitequark/groupXIV/d2140be3acbdd20286f9eddc5ec10b8db67b817a/public_html/vendor/leaflet.draw/images/layers-2x.png -------------------------------------------------------------------------------- /public_html/vendor/leaflet.draw/images/layers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitequark/groupXIV/d2140be3acbdd20286f9eddc5ec10b8db67b817a/public_html/vendor/leaflet.draw/images/layers.png -------------------------------------------------------------------------------- /public_html/vendor/leaflet.draw/images/marker-icon-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitequark/groupXIV/d2140be3acbdd20286f9eddc5ec10b8db67b817a/public_html/vendor/leaflet.draw/images/marker-icon-2x.png -------------------------------------------------------------------------------- /public_html/vendor/leaflet.draw/images/marker-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitequark/groupXIV/d2140be3acbdd20286f9eddc5ec10b8db67b817a/public_html/vendor/leaflet.draw/images/marker-icon.png -------------------------------------------------------------------------------- /public_html/vendor/leaflet.draw/images/marker-shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitequark/groupXIV/d2140be3acbdd20286f9eddc5ec10b8db67b817a/public_html/vendor/leaflet.draw/images/marker-shadow.png -------------------------------------------------------------------------------- /public_html/vendor/leaflet.draw/images/spritesheet-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitequark/groupXIV/d2140be3acbdd20286f9eddc5ec10b8db67b817a/public_html/vendor/leaflet.draw/images/spritesheet-2x.png -------------------------------------------------------------------------------- /public_html/vendor/leaflet.draw/images/spritesheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitequark/groupXIV/d2140be3acbdd20286f9eddc5ec10b8db67b817a/public_html/vendor/leaflet.draw/images/spritesheet.png -------------------------------------------------------------------------------- /public_html/vendor/leaflet.draw/images/spritesheet.svg: -------------------------------------------------------------------------------- 1 | 2 | 21 | 23 | 24 | 26 | image/svg+xml 27 | 29 | 30 | 31 | 32 | 33 | 35 | 55 | 58 | 61 | 66 | 71 | 76 | 77 | 82 | 87 | 92 | 97 | 100 | 105 | 110 | 116 | 117 | 120 | 125 | 130 | 131 | 132 | 136 | 143 | 150 | 151 | 156 | 157 | -------------------------------------------------------------------------------- /public_html/vendor/leaflet.draw/leaflet.draw.css: -------------------------------------------------------------------------------- 1 | .leaflet-draw-section{position:relative}.leaflet-draw-toolbar{margin-top:12px}.leaflet-draw-toolbar-top{margin-top:0}.leaflet-draw-toolbar-notop a:first-child{border-top-right-radius:0}.leaflet-draw-toolbar-nobottom a:last-child{border-bottom-right-radius:0}.leaflet-draw-toolbar a{background-image:url('images/spritesheet.png');background-image:linear-gradient(transparent,transparent),url('images/spritesheet.svg');background-repeat:no-repeat;background-size:300px 30px;background-clip:padding-box}.leaflet-retina .leaflet-draw-toolbar a{background-image:url('images/spritesheet-2x.png');background-image:linear-gradient(transparent,transparent),url('images/spritesheet.svg')} 2 | .leaflet-draw a{display:block;text-align:center;text-decoration:none}.leaflet-draw a .sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.leaflet-draw-actions{display:none;list-style:none;margin:0;padding:0;position:absolute;left:26px;top:0;white-space:nowrap}.leaflet-touch .leaflet-draw-actions{left:32px}.leaflet-right .leaflet-draw-actions{right:26px;left:auto}.leaflet-touch .leaflet-right .leaflet-draw-actions{right:32px;left:auto}.leaflet-draw-actions li{display:inline-block} 3 | .leaflet-draw-actions li:first-child a{border-left:0}.leaflet-draw-actions li:last-child a{-webkit-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.leaflet-right .leaflet-draw-actions li:last-child a{-webkit-border-radius:0;border-radius:0}.leaflet-right .leaflet-draw-actions li:first-child a{-webkit-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.leaflet-draw-actions a{background-color:#919187;border-left:1px solid #AAA;color:#FFF;font:11px/19px "Helvetica Neue",Arial,Helvetica,sans-serif;line-height:28px;text-decoration:none;padding-left:10px;padding-right:10px;height:28px} 4 | .leaflet-touch .leaflet-draw-actions a{font-size:12px;line-height:30px;height:30px}.leaflet-draw-actions-bottom{margin-top:0}.leaflet-draw-actions-top{margin-top:1px}.leaflet-draw-actions-top a,.leaflet-draw-actions-bottom a{height:27px;line-height:27px}.leaflet-draw-actions a:hover{background-color:#a0a098}.leaflet-draw-actions-top.leaflet-draw-actions-bottom a{height:26px;line-height:26px}.leaflet-draw-toolbar .leaflet-draw-draw-polyline{background-position:-2px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-polyline{background-position:0 -1px} 5 | .leaflet-draw-toolbar .leaflet-draw-draw-polygon{background-position:-31px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-polygon{background-position:-29px -1px}.leaflet-draw-toolbar .leaflet-draw-draw-rectangle{background-position:-62px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-rectangle{background-position:-60px -1px}.leaflet-draw-toolbar .leaflet-draw-draw-circle{background-position:-92px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-circle{background-position:-90px -1px} 6 | .leaflet-draw-toolbar .leaflet-draw-draw-marker{background-position:-122px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-marker{background-position:-120px -1px}.leaflet-draw-toolbar .leaflet-draw-draw-circlemarker{background-position:-273px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-draw-circlemarker{background-position:-271px -1px}.leaflet-draw-toolbar .leaflet-draw-edit-edit{background-position:-152px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-edit{background-position:-150px -1px} 7 | .leaflet-draw-toolbar .leaflet-draw-edit-remove{background-position:-182px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-remove{background-position:-180px -1px}.leaflet-draw-toolbar .leaflet-draw-edit-edit.leaflet-disabled{background-position:-212px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-edit.leaflet-disabled{background-position:-210px -1px}.leaflet-draw-toolbar .leaflet-draw-edit-remove.leaflet-disabled{background-position:-242px -2px}.leaflet-touch .leaflet-draw-toolbar .leaflet-draw-edit-remove.leaflet-disabled{background-position:-240px -2px} 8 | .leaflet-mouse-marker{background-color:#fff;cursor:crosshair}.leaflet-draw-tooltip{background:#363636;background:rgba(0,0,0,0.5);border:1px solid transparent;-webkit-border-radius:4px;border-radius:4px;color:#fff;font:12px/18px "Helvetica Neue",Arial,Helvetica,sans-serif;margin-left:20px;margin-top:-21px;padding:4px 8px;position:absolute;visibility:hidden;white-space:nowrap;z-index:6}.leaflet-draw-tooltip:before{border-right:6px solid black;border-right-color:rgba(0,0,0,0.5);border-top:6px solid transparent;border-bottom:6px solid transparent;content:"";position:absolute;top:7px;left:-7px} 9 | .leaflet-error-draw-tooltip{background-color:#f2dede;border:1px solid #e6b6bd;color:#b94a48}.leaflet-error-draw-tooltip:before{border-right-color:#e6b6bd}.leaflet-draw-tooltip-single{margin-top:-12px}.leaflet-draw-tooltip-subtext{color:#f8d5e4}.leaflet-draw-guide-dash{font-size:1%;opacity:.6;position:absolute;width:5px;height:5px}.leaflet-edit-marker-selected{background-color:rgba(254,87,161,0.1);border:4px dashed rgba(254,87,161,0.6);-webkit-border-radius:4px;border-radius:4px;box-sizing:content-box} 10 | .leaflet-edit-move{cursor:move}.leaflet-edit-resize{cursor:pointer}.leaflet-oldie .leaflet-draw-toolbar{border:1px solid #999} -------------------------------------------------------------------------------- /public_html/vendor/leaflet.draw/leaflet.draw.js: -------------------------------------------------------------------------------- 1 | /* 2 | Leaflet.draw 1.0.4, a plugin that adds drawing and editing tools to Leaflet powered maps. 3 | (c) 2012-2017, Jacob Toye, Jon West, Smartrak, Leaflet 4 | 5 | https://github.com/Leaflet/Leaflet.draw 6 | http://leafletjs.com 7 | */ 8 | !function(t,e,i){function o(t,e){for(;(t=t.parentElement)&&!t.classList.contains(e););return t}L.drawVersion="1.0.4",L.Draw={},L.drawLocal={draw:{toolbar:{actions:{title:"Cancel drawing",text:"Cancel"},finish:{title:"Finish drawing",text:"Finish"},undo:{title:"Delete last point drawn",text:"Delete last point"},buttons:{polyline:"Draw a polyline",polygon:"Draw a polygon",rectangle:"Draw a rectangle",circle:"Draw a circle",marker:"Draw a marker",circlemarker:"Draw a circlemarker"}},handlers:{circle:{tooltip:{start:"Click and drag to draw circle."},radius:"Radius"},circlemarker:{tooltip:{start:"Click map to place circle marker."}},marker:{tooltip:{start:"Click map to place marker."}},polygon:{tooltip:{start:"Click to start drawing shape.",cont:"Click to continue drawing shape.",end:"Click first point to close this shape."}},polyline:{error:"Error: shape edges cannot cross!",tooltip:{start:"Click to start drawing line.",cont:"Click to continue drawing line.",end:"Click last point to finish line."}},rectangle:{tooltip:{start:"Click and drag to draw rectangle."}},simpleshape:{tooltip:{end:"Release mouse to finish drawing."}}}},edit:{toolbar:{actions:{save:{title:"Save changes",text:"Save"},cancel:{title:"Cancel editing, discards all changes",text:"Cancel"},clearAll:{title:"Clear all layers",text:"Clear All"}},buttons:{edit:"Edit layers",editDisabled:"No layers to edit",remove:"Delete layers",removeDisabled:"No layers to delete"}},handlers:{edit:{tooltip:{text:"Drag handles or markers to edit features.",subtext:"Click cancel to undo changes."}},remove:{tooltip:{text:"Click on a feature to remove."}}}}},L.Draw.Event={},L.Draw.Event.CREATED="draw:created",L.Draw.Event.EDITED="draw:edited",L.Draw.Event.DELETED="draw:deleted",L.Draw.Event.DRAWSTART="draw:drawstart",L.Draw.Event.DRAWSTOP="draw:drawstop",L.Draw.Event.DRAWVERTEX="draw:drawvertex",L.Draw.Event.EDITSTART="draw:editstart",L.Draw.Event.EDITMOVE="draw:editmove",L.Draw.Event.EDITRESIZE="draw:editresize",L.Draw.Event.EDITVERTEX="draw:editvertex",L.Draw.Event.EDITSTOP="draw:editstop",L.Draw.Event.DELETESTART="draw:deletestart",L.Draw.Event.DELETESTOP="draw:deletestop",L.Draw.Event.TOOLBAROPENED="draw:toolbaropened",L.Draw.Event.TOOLBARCLOSED="draw:toolbarclosed",L.Draw.Event.MARKERCONTEXT="draw:markercontext",L.Draw=L.Draw||{},L.Draw.Feature=L.Handler.extend({initialize:function(t,e){this._map=t,this._container=t._container,this._overlayPane=t._panes.overlayPane,this._popupPane=t._panes.popupPane,e&&e.shapeOptions&&(e.shapeOptions=L.Util.extend({},this.options.shapeOptions,e.shapeOptions)),L.setOptions(this,e);var i=L.version.split(".");1===parseInt(i[0],10)&&parseInt(i[1],10)>=2?L.Draw.Feature.include(L.Evented.prototype):L.Draw.Feature.include(L.Mixin.Events)},enable:function(){this._enabled||(L.Handler.prototype.enable.call(this),this.fire("enabled",{handler:this.type}),this._map.fire(L.Draw.Event.DRAWSTART,{layerType:this.type}))},disable:function(){this._enabled&&(L.Handler.prototype.disable.call(this),this._map.fire(L.Draw.Event.DRAWSTOP,{layerType:this.type}),this.fire("disabled",{handler:this.type}))},addHooks:function(){var t=this._map;t&&(L.DomUtil.disableTextSelection(),t.getContainer().focus(),this._tooltip=new L.Draw.Tooltip(this._map),L.DomEvent.on(this._container,"keyup",this._cancelDrawing,this))},removeHooks:function(){this._map&&(L.DomUtil.enableTextSelection(),this._tooltip.dispose(),this._tooltip=null,L.DomEvent.off(this._container,"keyup",this._cancelDrawing,this))},setOptions:function(t){L.setOptions(this,t)},_fireCreatedEvent:function(t){this._map.fire(L.Draw.Event.CREATED,{layer:t,layerType:this.type})},_cancelDrawing:function(t){27===t.keyCode&&(this._map.fire("draw:canceled",{layerType:this.type}),this.disable())}}),L.Draw.Polyline=L.Draw.Feature.extend({statics:{TYPE:"polyline"},Poly:L.Polyline,options:{allowIntersection:!0,repeatMode:!1,drawError:{color:"#b00b00",timeout:2500},icon:new L.DivIcon({iconSize:new L.Point(8,8),className:"leaflet-div-icon leaflet-editing-icon"}),touchIcon:new L.DivIcon({iconSize:new L.Point(20,20),className:"leaflet-div-icon leaflet-editing-icon leaflet-touch-icon"}),guidelineDistance:20,maxGuideLineLength:4e3,shapeOptions:{stroke:!0,color:"#3388ff",weight:4,opacity:.5,fill:!1,clickable:!0},metric:!0,feet:!0,nautic:!1,showLength:!0,zIndexOffset:2e3,factor:1,maxPoints:0},initialize:function(t,e){L.Browser.touch&&(this.options.icon=this.options.touchIcon),this.options.drawError.message=L.drawLocal.draw.handlers.polyline.error,e&&e.drawError&&(e.drawError=L.Util.extend({},this.options.drawError,e.drawError)),this.type=L.Draw.Polyline.TYPE,L.Draw.Feature.prototype.initialize.call(this,t,e)},addHooks:function(){L.Draw.Feature.prototype.addHooks.call(this),this._map&&(this._markers=[],this._markerGroup=new L.LayerGroup,this._map.addLayer(this._markerGroup),this._poly=new L.Polyline([],this.options.shapeOptions),this._tooltip.updateContent(this._getTooltipText()),this._mouseMarker||(this._mouseMarker=L.marker(this._map.getCenter(),{icon:L.divIcon({className:"leaflet-mouse-marker",iconAnchor:[20,20],iconSize:[40,40]}),opacity:0,zIndexOffset:this.options.zIndexOffset})),this._mouseMarker.on("mouseout",this._onMouseOut,this).on("mousemove",this._onMouseMove,this).on("mousedown",this._onMouseDown,this).on("mouseup",this._onMouseUp,this).addTo(this._map),this._map.on("mouseup",this._onMouseUp,this).on("mousemove",this._onMouseMove,this).on("zoomlevelschange",this._onZoomEnd,this).on("touchstart",this._onTouch,this).on("zoomend",this._onZoomEnd,this))},removeHooks:function(){L.Draw.Feature.prototype.removeHooks.call(this),this._clearHideErrorTimeout(),this._cleanUpShape(),this._map.removeLayer(this._markerGroup),delete this._markerGroup,delete this._markers,this._map.removeLayer(this._poly),delete this._poly,this._mouseMarker.off("mousedown",this._onMouseDown,this).off("mouseout",this._onMouseOut,this).off("mouseup",this._onMouseUp,this).off("mousemove",this._onMouseMove,this),this._map.removeLayer(this._mouseMarker),delete this._mouseMarker,this._clearGuides(),this._map.off("mouseup",this._onMouseUp,this).off("mousemove",this._onMouseMove,this).off("zoomlevelschange",this._onZoomEnd,this).off("zoomend",this._onZoomEnd,this).off("touchstart",this._onTouch,this).off("click",this._onTouch,this)},deleteLastVertex:function(){if(!(this._markers.length<=1)){var t=this._markers.pop(),e=this._poly,i=e.getLatLngs(),o=i.splice(-1,1)[0];this._poly.setLatLngs(i),this._markerGroup.removeLayer(t),e.getLatLngs().length<2&&this._map.removeLayer(e),this._vertexChanged(o,!1)}},addVertex:function(t){if(this._markers.length>=2&&!this.options.allowIntersection&&this._poly.newLatLngIntersects(t))return void this._showErrorTooltip();this._errorShown&&this._hideErrorTooltip(),this._markers.push(this._createMarker(t)),this._poly.addLatLng(t),2===this._poly.getLatLngs().length&&this._map.addLayer(this._poly),this._vertexChanged(t,!0)},completeShape:function(){this._markers.length<=1||!this._shapeIsValid()||(this._fireCreatedEvent(),this.disable(),this.options.repeatMode&&this.enable())},_finishShape:function(){var t=this._poly._defaultShape?this._poly._defaultShape():this._poly.getLatLngs(),e=this._poly.newLatLngIntersects(t[t.length-1]);if(!this.options.allowIntersection&&e||!this._shapeIsValid())return void this._showErrorTooltip();this._fireCreatedEvent(),this.disable(),this.options.repeatMode&&this.enable()},_shapeIsValid:function(){return!0},_onZoomEnd:function(){null!==this._markers&&this._updateGuide()},_onMouseMove:function(t){var e=this._map.mouseEventToLayerPoint(t.originalEvent),i=this._map.layerPointToLatLng(e);this._currentLatLng=i,this._updateTooltip(i),this._updateGuide(e),this._mouseMarker.setLatLng(i),L.DomEvent.preventDefault(t.originalEvent)},_vertexChanged:function(t,e){this._map.fire(L.Draw.Event.DRAWVERTEX,{layers:this._markerGroup}),this._updateFinishHandler(),this._updateRunningMeasure(t,e),this._clearGuides(),this._updateTooltip()},_onMouseDown:function(t){if(!this._clickHandled&&!this._touchHandled&&!this._disableMarkers){this._onMouseMove(t),this._clickHandled=!0,this._disableNewMarkers();var e=t.originalEvent,i=e.clientX,o=e.clientY;this._startPoint.call(this,i,o)}},_startPoint:function(t,e){this._mouseDownOrigin=L.point(t,e)},_onMouseUp:function(t){var e=t.originalEvent,i=e.clientX,o=e.clientY;this._endPoint.call(this,i,o,t),this._clickHandled=null},_endPoint:function(e,i,o){if(this._mouseDownOrigin){var a=L.point(e,i).distanceTo(this._mouseDownOrigin),n=this._calculateFinishDistance(o.latlng);this.options.maxPoints>1&&this.options.maxPoints==this._markers.length+1?(this.addVertex(o.latlng),this._finishShape()):n<10&&L.Browser.touch?this._finishShape():Math.abs(a)<9*(t.devicePixelRatio||1)&&this.addVertex(o.latlng),this._enableNewMarkers()}this._mouseDownOrigin=null},_onTouch:function(t){var e,i,o=t.originalEvent;!o.touches||!o.touches[0]||this._clickHandled||this._touchHandled||this._disableMarkers||(e=o.touches[0].clientX,i=o.touches[0].clientY,this._disableNewMarkers(),this._touchHandled=!0,this._startPoint.call(this,e,i),this._endPoint.call(this,e,i,t),this._touchHandled=null),this._clickHandled=null},_onMouseOut:function(){this._tooltip&&this._tooltip._onMouseOut.call(this._tooltip)},_calculateFinishDistance:function(t){var e;if(this._markers.length>0){var i;if(this.type===L.Draw.Polyline.TYPE)i=this._markers[this._markers.length-1];else{if(this.type!==L.Draw.Polygon.TYPE)return 1/0;i=this._markers[0]}var o=this._map.latLngToContainerPoint(i.getLatLng()),a=new L.Marker(t,{icon:this.options.icon,zIndexOffset:2*this.options.zIndexOffset}),n=this._map.latLngToContainerPoint(a.getLatLng());e=o.distanceTo(n)}else e=1/0;return e},_updateFinishHandler:function(){var t=this._markers.length;t>1&&this._markers[t-1].on("click",this._finishShape,this),t>2&&this._markers[t-2].off("click",this._finishShape,this)},_createMarker:function(t){var e=new L.Marker(t,{icon:this.options.icon,zIndexOffset:2*this.options.zIndexOffset});return this._markerGroup.addLayer(e),e},_updateGuide:function(t){var e=this._markers?this._markers.length:0;e>0&&(t=t||this._map.latLngToLayerPoint(this._currentLatLng),this._clearGuides(),this._drawGuide(this._map.latLngToLayerPoint(this._markers[e-1].getLatLng()),t))},_updateTooltip:function(t){var e=this._getTooltipText();t&&this._tooltip.updatePosition(t),this._errorShown||this._tooltip.updateContent(e)},_drawGuide:function(t,e){var i,o,a,n=Math.floor(Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2))),s=this.options.guidelineDistance,r=this.options.maxGuideLineLength,l=n>r?n-r:s;for(this._guidesContainer||(this._guidesContainer=L.DomUtil.create("div","leaflet-draw-guides",this._overlayPane));l1&&this._markers[this._markers.length-1].off("click",this._finishShape,this)},_fireCreatedEvent:function(){var t=new this.Poly(this._poly.getLatLngs(),this.options.shapeOptions);L.Draw.Feature.prototype._fireCreatedEvent.call(this,t)}}),L.Draw.Polygon=L.Draw.Polyline.extend({statics:{TYPE:"polygon"},Poly:L.Polygon,options:{showArea:!1,showLength:!1,shapeOptions:{stroke:!0,color:"#3388ff",weight:4,opacity:.5,fill:!0,fillColor:null,fillOpacity:.2,clickable:!0},metric:!0,feet:!0,nautic:!1,precision:{}},initialize:function(t,e){L.Draw.Polyline.prototype.initialize.call(this,t,e),this.type=L.Draw.Polygon.TYPE},_updateFinishHandler:function(){var t=this._markers.length;1===t&&this._markers[0].on("click",this._finishShape,this),t>2&&(this._markers[t-1].on("dblclick",this._finishShape,this),t>3&&this._markers[t-2].off("dblclick",this._finishShape,this))},_getTooltipText:function(){var t,e;return 0===this._markers.length?t=L.drawLocal.draw.handlers.polygon.tooltip.start:this._markers.length<3?(t=L.drawLocal.draw.handlers.polygon.tooltip.cont,e=this._getMeasurementString()):(t=L.drawLocal.draw.handlers.polygon.tooltip.end,e=this._getMeasurementString()),{text:t,subtext:e}},_getMeasurementString:function(){var t=this._area,e="";return t||this.options.showLength?(this.options.showLength&&(e=L.Draw.Polyline.prototype._getMeasurementString.call(this)),t&&(e+="
"+L.GeometryUtil.readableArea(t,this.options.metric,this.options.precision)),e):null},_shapeIsValid:function(){return this._markers.length>=3},_vertexChanged:function(t,e){var i;!this.options.allowIntersection&&this.options.showArea&&(i=this._poly.getLatLngs(),this._area=L.GeometryUtil.geodesicArea(i)),L.Draw.Polyline.prototype._vertexChanged.call(this,t,e)},_cleanUpShape:function(){var t=this._markers.length;t>0&&(this._markers[0].off("click",this._finishShape,this),t>2&&this._markers[t-1].off("dblclick",this._finishShape,this))}}),L.SimpleShape={},L.Draw.SimpleShape=L.Draw.Feature.extend({options:{repeatMode:!1},initialize:function(t,e){this._endLabelText=L.drawLocal.draw.handlers.simpleshape.tooltip.end,L.Draw.Feature.prototype.initialize.call(this,t,e)},addHooks:function(){L.Draw.Feature.prototype.addHooks.call(this),this._map&&(this._mapDraggable=this._map.dragging.enabled(),this._mapDraggable&&this._map.dragging.disable(),this._container.style.cursor="crosshair",this._tooltip.updateContent({text:this._initialLabelText}),this._map.on("mousedown",this._onMouseDown,this).on("mousemove",this._onMouseMove,this).on("touchstart",this._onMouseDown,this).on("touchmove",this._onMouseMove,this),e.addEventListener("touchstart",L.DomEvent.preventDefault,{passive:!1}))},removeHooks:function(){L.Draw.Feature.prototype.removeHooks.call(this),this._map&&(this._mapDraggable&&this._map.dragging.enable(),this._container.style.cursor="",this._map.off("mousedown",this._onMouseDown,this).off("mousemove",this._onMouseMove,this).off("touchstart",this._onMouseDown,this).off("touchmove",this._onMouseMove,this),L.DomEvent.off(e,"mouseup",this._onMouseUp,this),L.DomEvent.off(e,"touchend",this._onMouseUp,this),e.removeEventListener("touchstart",L.DomEvent.preventDefault),this._shape&&(this._map.removeLayer(this._shape),delete this._shape)),this._isDrawing=!1},_getTooltipText:function(){return{text:this._endLabelText}},_onMouseDown:function(t){this._isDrawing=!0,this._startLatLng=t.latlng,L.DomEvent.on(e,"mouseup",this._onMouseUp,this).on(e,"touchend",this._onMouseUp,this).preventDefault(t.originalEvent)},_onMouseMove:function(t){var e=t.latlng;this._tooltip.updatePosition(e),this._isDrawing&&(this._tooltip.updateContent(this._getTooltipText()),this._drawShape(e))},_onMouseUp:function(){this._shape&&this._fireCreatedEvent(),this.disable(),this.options.repeatMode&&this.enable()}}),L.Draw.Rectangle=L.Draw.SimpleShape.extend({statics:{TYPE:"rectangle"},options:{shapeOptions:{stroke:!0,color:"#3388ff",weight:4,opacity:.5,fill:!0,fillColor:null,fillOpacity:.2,clickable:!0},showArea:!0,metric:!0},initialize:function(t,e){this.type=L.Draw.Rectangle.TYPE,this._initialLabelText=L.drawLocal.draw.handlers.rectangle.tooltip.start,L.Draw.SimpleShape.prototype.initialize.call(this,t,e)},disable:function(){this._enabled&&(this._isCurrentlyTwoClickDrawing=!1,L.Draw.SimpleShape.prototype.disable.call(this))},_onMouseUp:function(t){if(!this._shape&&!this._isCurrentlyTwoClickDrawing)return void(this._isCurrentlyTwoClickDrawing=!0);this._isCurrentlyTwoClickDrawing&&!o(t.target,"leaflet-pane")||L.Draw.SimpleShape.prototype._onMouseUp.call(this)},_drawShape:function(t){this._shape?this._shape.setBounds(new L.LatLngBounds(this._startLatLng,t)):(this._shape=new L.Rectangle(new L.LatLngBounds(this._startLatLng,t),this.options.shapeOptions),this._map.addLayer(this._shape))},_fireCreatedEvent:function(){var t=new L.Rectangle(this._shape.getBounds(),this.options.shapeOptions);L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this,t)},_getTooltipText:function(){var t,e,i,o=L.Draw.SimpleShape.prototype._getTooltipText.call(this),a=this._shape,n=this.options.showArea;return a&&(t=this._shape._defaultShape?this._shape._defaultShape():this._shape.getLatLngs(),e=L.GeometryUtil.geodesicArea(t),i=n?L.GeometryUtil.readableArea(e,this.options.metric):""),{text:o.text,subtext:i}}}),L.Draw.Marker=L.Draw.Feature.extend({statics:{TYPE:"marker"},options:{icon:new L.Icon.Default,repeatMode:!1,zIndexOffset:2e3},initialize:function(t,e){this.type=L.Draw.Marker.TYPE,this._initialLabelText=L.drawLocal.draw.handlers.marker.tooltip.start,L.Draw.Feature.prototype.initialize.call(this,t,e)},addHooks:function(){L.Draw.Feature.prototype.addHooks.call(this),this._map&&(this._tooltip.updateContent({text:this._initialLabelText}),this._mouseMarker||(this._mouseMarker=L.marker(this._map.getCenter(),{icon:L.divIcon({className:"leaflet-mouse-marker",iconAnchor:[20,20],iconSize:[40,40]}),opacity:0,zIndexOffset:this.options.zIndexOffset})),this._mouseMarker.on("click",this._onClick,this).addTo(this._map),this._map.on("mousemove",this._onMouseMove,this),this._map.on("click",this._onTouch,this))},removeHooks:function(){L.Draw.Feature.prototype.removeHooks.call(this),this._map&&(this._map.off("click",this._onClick,this).off("click",this._onTouch,this),this._marker&&(this._marker.off("click",this._onClick,this),this._map.removeLayer(this._marker),delete this._marker),this._mouseMarker.off("click",this._onClick,this),this._map.removeLayer(this._mouseMarker),delete this._mouseMarker,this._map.off("mousemove",this._onMouseMove,this))},_onMouseMove:function(t){var e=t.latlng;this._tooltip.updatePosition(e),this._mouseMarker.setLatLng(e),this._marker?(e=this._mouseMarker.getLatLng(),this._marker.setLatLng(e)):(this._marker=this._createMarker(e),this._marker.on("click",this._onClick,this),this._map.on("click",this._onClick,this).addLayer(this._marker))},_createMarker:function(t){return new L.Marker(t,{icon:this.options.icon,zIndexOffset:this.options.zIndexOffset})},_onClick:function(){this._fireCreatedEvent(),this.disable(),this.options.repeatMode&&this.enable()},_onTouch:function(t){this._onMouseMove(t),this._onClick()},_fireCreatedEvent:function(){var t=new L.Marker.Touch(this._marker.getLatLng(),{icon:this.options.icon});L.Draw.Feature.prototype._fireCreatedEvent.call(this,t)}}),L.Draw.CircleMarker=L.Draw.Marker.extend({statics:{TYPE:"circlemarker"},options:{stroke:!0,color:"#3388ff",weight:4,opacity:.5,fill:!0,fillColor:null,fillOpacity:.2,clickable:!0,zIndexOffset:2e3},initialize:function(t,e){this.type=L.Draw.CircleMarker.TYPE,this._initialLabelText=L.drawLocal.draw.handlers.circlemarker.tooltip.start,L.Draw.Feature.prototype.initialize.call(this,t,e)},_fireCreatedEvent:function(){var t=new L.CircleMarker(this._marker.getLatLng(),this.options);L.Draw.Feature.prototype._fireCreatedEvent.call(this,t)},_createMarker:function(t){return new L.CircleMarker(t,this.options)}}),L.Draw.Circle=L.Draw.SimpleShape.extend({statics:{TYPE:"circle"},options:{shapeOptions:{stroke:!0,color:"#3388ff",weight:4,opacity:.5,fill:!0,fillColor:null,fillOpacity:.2,clickable:!0},showRadius:!0,metric:!0,feet:!0,nautic:!1},initialize:function(t,e){this.type=L.Draw.Circle.TYPE,this._initialLabelText=L.drawLocal.draw.handlers.circle.tooltip.start,L.Draw.SimpleShape.prototype.initialize.call(this,t,e)},_drawShape:function(t){if(L.GeometryUtil.isVersion07x())var e=this._startLatLng.distanceTo(t);else var e=this._map.distance(this._startLatLng,t);this._shape?this._shape.setRadius(e):(this._shape=new L.Circle(this._startLatLng,e,this.options.shapeOptions),this._map.addLayer(this._shape))},_fireCreatedEvent:function(){var t=new L.Circle(this._startLatLng,this._shape.getRadius(),this.options.shapeOptions);L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this,t)},_onMouseMove:function(t){var e,i=t.latlng,o=this.options.showRadius,a=this.options.metric;if(this._tooltip.updatePosition(i),this._isDrawing){this._drawShape(i),e=this._shape.getRadius().toFixed(1);var n="";o&&(n=L.drawLocal.draw.handlers.circle.radius+": "+L.GeometryUtil.readableDistance(e,a,this.options.feet,this.options.nautic)),this._tooltip.updateContent({text:this._endLabelText,subtext:n})}}}),L.Edit=L.Edit||{},L.Edit.Marker=L.Handler.extend({initialize:function(t,e){this._marker=t,L.setOptions(this,e)},addHooks:function(){var t=this._marker;t.dragging.enable(),t.on("dragend",this._onDragEnd,t),this._toggleMarkerHighlight()},removeHooks:function(){var t=this._marker;t.dragging.disable(),t.off("dragend",this._onDragEnd,t),this._toggleMarkerHighlight()},_onDragEnd:function(t){var e=t.target;e.edited=!0,this._map.fire(L.Draw.Event.EDITMOVE,{layer:e})},_toggleMarkerHighlight:function(){var t=this._marker._icon;t&&(t.style.display="none",L.DomUtil.hasClass(t,"leaflet-edit-marker-selected")?(L.DomUtil.removeClass(t,"leaflet-edit-marker-selected"),this._offsetMarker(t,-4)):(L.DomUtil.addClass(t,"leaflet-edit-marker-selected"),this._offsetMarker(t,4)),t.style.display="")},_offsetMarker:function(t,e){var i=parseInt(t.style.marginTop,10)-e,o=parseInt(t.style.marginLeft,10)-e;t.style.marginTop=i+"px",t.style.marginLeft=o+"px"}}),L.Marker.addInitHook(function(){L.Edit.Marker&&(this.editing=new L.Edit.Marker(this),this.options.editable&&this.editing.enable())}),L.Edit=L.Edit||{},L.Edit.Poly=L.Handler.extend({initialize:function(t){this.latlngs=[t._latlngs],t._holes&&(this.latlngs=this.latlngs.concat(t._holes)),this._poly=t,this._poly.on("revert-edited",this._updateLatLngs,this)},_defaultShape:function(){return L.Polyline._flat?L.Polyline._flat(this._poly._latlngs)?this._poly._latlngs:this._poly._latlngs[0]:this._poly._latlngs},_eachVertexHandler:function(t){for(var e=0;et&&(i._index+=e)})},_createMiddleMarker:function(t,e){var i,o,a,n=this._getMiddleLatLng(t,e),s=this._createMarker(n);s.setOpacity(.6),t._middleRight=e._middleLeft=s,o=function(){s.off("touchmove",o,this);var a=e._index;s._index=a,s.off("click",i,this).on("click",this._onMarkerClick,this),n.lat=s.getLatLng().lat,n.lng=s.getLatLng().lng,this._spliceLatLngs(a,0,n),this._markers.splice(a,0,s),s.setOpacity(1),this._updateIndexes(a,1),e._index++,this._updatePrevNext(t,s),this._updatePrevNext(s,e),this._poly.fire("editstart")},a=function(){s.off("dragstart",o,this),s.off("dragend",a,this),s.off("touchmove",o,this),this._createMiddleMarker(t,s),this._createMiddleMarker(s,e)},i=function(){o.call(this),a.call(this),this._fireEdit()},s.on("click",i,this).on("dragstart",o,this).on("dragend",a,this).on("touchmove",o,this),this._markerGroup.addLayer(s)},_updatePrevNext:function(t,e){t&&(t._next=e),e&&(e._prev=t)},_getMiddleLatLng:function(t,e){var i=this._poly._map,o=i.project(t.getLatLng()),a=i.project(e.getLatLng());return i.unproject(o._add(a)._divideBy(2))}}),L.Polyline.addInitHook(function(){this.editing||(L.Edit.Poly&&(this.editing=new L.Edit.Poly(this),this.options.editable&&this.editing.enable()),this.on("add",function(){this.editing&&this.editing.enabled()&&this.editing.addHooks()}),this.on("remove",function(){this.editing&&this.editing.enabled()&&this.editing.removeHooks()}))}),L.Edit=L.Edit||{},L.Edit.SimpleShape=L.Handler.extend({options:{moveIcon:new L.DivIcon({iconSize:new L.Point(8,8),className:"leaflet-div-icon leaflet-editing-icon leaflet-edit-move"}),resizeIcon:new L.DivIcon({iconSize:new L.Point(8,8), 9 | className:"leaflet-div-icon leaflet-editing-icon leaflet-edit-resize"}),touchMoveIcon:new L.DivIcon({iconSize:new L.Point(20,20),className:"leaflet-div-icon leaflet-editing-icon leaflet-edit-move leaflet-touch-icon"}),touchResizeIcon:new L.DivIcon({iconSize:new L.Point(20,20),className:"leaflet-div-icon leaflet-editing-icon leaflet-edit-resize leaflet-touch-icon"})},initialize:function(t,e){L.Browser.touch&&(this.options.moveIcon=this.options.touchMoveIcon,this.options.resizeIcon=this.options.touchResizeIcon),this._shape=t,L.Util.setOptions(this,e)},addHooks:function(){var t=this._shape;this._shape._map&&(this._map=this._shape._map,t.setStyle(t.options.editing),t._map&&(this._map=t._map,this._markerGroup||this._initMarkers(),this._map.addLayer(this._markerGroup)))},removeHooks:function(){var t=this._shape;if(t.setStyle(t.options.original),t._map){this._unbindMarker(this._moveMarker);for(var e=0,i=this._resizeMarkers.length;e"+L.drawLocal.edit.handlers.edit.tooltip.text,subtext:L.drawLocal.draw.handlers.circle.radius+": "+L.GeometryUtil.readableDistance(radius,!0,this.options.feet,this.options.nautic)}),this._shape.setRadius(radius),this._map.fire(L.Draw.Event.EDITRESIZE,{layer:this._shape})}}),L.Circle.addInitHook(function(){L.Edit.Circle&&(this.editing=new L.Edit.Circle(this),this.options.editable&&this.editing.enable())}),L.Map.mergeOptions({touchExtend:!0}),L.Map.TouchExtend=L.Handler.extend({initialize:function(t){this._map=t,this._container=t._container,this._pane=t._panes.overlayPane},addHooks:function(){L.DomEvent.on(this._container,"touchstart",this._onTouchStart,this),L.DomEvent.on(this._container,"touchend",this._onTouchEnd,this),L.DomEvent.on(this._container,"touchmove",this._onTouchMove,this),this._detectIE()?(L.DomEvent.on(this._container,"MSPointerDown",this._onTouchStart,this),L.DomEvent.on(this._container,"MSPointerUp",this._onTouchEnd,this),L.DomEvent.on(this._container,"MSPointerMove",this._onTouchMove,this),L.DomEvent.on(this._container,"MSPointerCancel",this._onTouchCancel,this)):(L.DomEvent.on(this._container,"touchcancel",this._onTouchCancel,this),L.DomEvent.on(this._container,"touchleave",this._onTouchLeave,this))},removeHooks:function(){L.DomEvent.off(this._container,"touchstart",this._onTouchStart,this),L.DomEvent.off(this._container,"touchend",this._onTouchEnd,this),L.DomEvent.off(this._container,"touchmove",this._onTouchMove,this),this._detectIE()?(L.DomEvent.off(this._container,"MSPointerDown",this._onTouchStart,this),L.DomEvent.off(this._container,"MSPointerUp",this._onTouchEnd,this),L.DomEvent.off(this._container,"MSPointerMove",this._onTouchMove,this),L.DomEvent.off(this._container,"MSPointerCancel",this._onTouchCancel,this)):(L.DomEvent.off(this._container,"touchcancel",this._onTouchCancel,this),L.DomEvent.off(this._container,"touchleave",this._onTouchLeave,this))},_touchEvent:function(t,e){var i={};if(void 0!==t.touches){if(!t.touches.length)return;i=t.touches[0]}else{if("touch"!==t.pointerType)return;if(i=t,!this._filterClick(t))return}var o=this._map.mouseEventToContainerPoint(i),a=this._map.mouseEventToLayerPoint(i),n=this._map.layerPointToLatLng(a);this._map.fire(e,{latlng:n,layerPoint:a,containerPoint:o,pageX:i.pageX,pageY:i.pageY,originalEvent:t})},_filterClick:function(t){var e=t.timeStamp||t.originalEvent.timeStamp,i=L.DomEvent._lastClick&&e-L.DomEvent._lastClick;return i&&i>100&&i<500||t.target._simulatedClick&&!t._simulated?(L.DomEvent.stop(t),!1):(L.DomEvent._lastClick=e,!0)},_onTouchStart:function(t){if(this._map._loaded){this._touchEvent(t,"touchstart")}},_onTouchEnd:function(t){if(this._map._loaded){this._touchEvent(t,"touchend")}},_onTouchCancel:function(t){if(this._map._loaded){var e="touchcancel";this._detectIE()&&(e="pointercancel"),this._touchEvent(t,e)}},_onTouchLeave:function(t){if(this._map._loaded){this._touchEvent(t,"touchleave")}},_onTouchMove:function(t){if(this._map._loaded){this._touchEvent(t,"touchmove")}},_detectIE:function(){var e=t.navigator.userAgent,i=e.indexOf("MSIE ");if(i>0)return parseInt(e.substring(i+5,e.indexOf(".",i)),10);if(e.indexOf("Trident/")>0){var o=e.indexOf("rv:");return parseInt(e.substring(o+3,e.indexOf(".",o)),10)}var a=e.indexOf("Edge/");return a>0&&parseInt(e.substring(a+5,e.indexOf(".",a)),10)}}),L.Map.addInitHook("addHandler","touchExtend",L.Map.TouchExtend),L.Marker.Touch=L.Marker.extend({_initInteraction:function(){return this.addInteractiveTarget?L.Marker.prototype._initInteraction.apply(this):this._initInteractionLegacy()},_initInteractionLegacy:function(){if(this.options.clickable){var t=this._icon,e=["dblclick","mousedown","mouseover","mouseout","contextmenu","touchstart","touchend","touchmove"];this._detectIE?e.concat(["MSPointerDown","MSPointerUp","MSPointerMove","MSPointerCancel"]):e.concat(["touchcancel"]),L.DomUtil.addClass(t,"leaflet-clickable"),L.DomEvent.on(t,"click",this._onMouseClick,this),L.DomEvent.on(t,"keypress",this._onKeyPress,this);for(var i=0;i0)return parseInt(e.substring(i+5,e.indexOf(".",i)),10);if(e.indexOf("Trident/")>0){var o=e.indexOf("rv:");return parseInt(e.substring(o+3,e.indexOf(".",o)),10)}var a=e.indexOf("Edge/");return a>0&&parseInt(e.substring(a+5,e.indexOf(".",a)),10)}}),L.LatLngUtil={cloneLatLngs:function(t){for(var e=[],i=0,o=t.length;i2){for(var s=0;s1&&(i=i+s+r[1])}return i},readableArea:function(e,i,o){var a,n,o=L.Util.extend({},t,o);return i?(n=["ha","m"],type=typeof i,"string"===type?n=[i]:"boolean"!==type&&(n=i),a=e>=1e6&&-1!==n.indexOf("km")?L.GeometryUtil.formattedNumber(1e-6*e,o.km)+" km²":e>=1e4&&-1!==n.indexOf("ha")?L.GeometryUtil.formattedNumber(1e-4*e,o.ha)+" ha":L.GeometryUtil.formattedNumber(e,o.m)+" m²"):(e/=.836127,a=e>=3097600?L.GeometryUtil.formattedNumber(e/3097600,o.mi)+" mi²":e>=4840?L.GeometryUtil.formattedNumber(e/4840,o.ac)+" acres":L.GeometryUtil.formattedNumber(e,o.yd)+" yd²"),a},readableDistance:function(e,i,o,a,n){var s,n=L.Util.extend({},t,n);switch(i?"string"==typeof i?i:"metric":o?"feet":a?"nauticalMile":"yards"){case"metric":s=e>1e3?L.GeometryUtil.formattedNumber(e/1e3,n.km)+" km":L.GeometryUtil.formattedNumber(e,n.m)+" m";break;case"feet":e*=3.28083,s=L.GeometryUtil.formattedNumber(e,n.ft)+" ft";break;case"nauticalMile":e*=.53996,s=L.GeometryUtil.formattedNumber(e/1e3,n.nm)+" nm";break;case"yards":default:e*=1.09361,s=e>1760?L.GeometryUtil.formattedNumber(e/1760,n.mi)+" miles":L.GeometryUtil.formattedNumber(e,n.yd)+" yd"}return s},isVersion07x:function(){var t=L.version.split(".");return 0===parseInt(t[0],10)&&7===parseInt(t[1],10)}})}(),L.Util.extend(L.LineUtil,{segmentsIntersect:function(t,e,i,o){return this._checkCounterclockwise(t,i,o)!==this._checkCounterclockwise(e,i,o)&&this._checkCounterclockwise(t,e,i)!==this._checkCounterclockwise(t,e,o)},_checkCounterclockwise:function(t,e,i){return(i.y-t.y)*(e.x-t.x)>(e.y-t.y)*(i.x-t.x)}}),L.Polyline.include({intersects:function(){var t,e,i,o=this._getProjectedPoints(),a=o?o.length:0;if(this._tooFewPointsForIntersection())return!1;for(t=a-1;t>=3;t--)if(e=o[t-1],i=o[t],this._lineSegmentsIntersectsRange(e,i,t-2))return!0;return!1},newLatLngIntersects:function(t,e){return!!this._map&&this.newPointIntersects(this._map.latLngToLayerPoint(t),e)},newPointIntersects:function(t,e){var i=this._getProjectedPoints(),o=i?i.length:0,a=i?i[o-1]:null,n=o-2;return!this._tooFewPointsForIntersection(1)&&this._lineSegmentsIntersectsRange(a,t,n,e?1:0)},_tooFewPointsForIntersection:function(t){var e=this._getProjectedPoints(),i=e?e.length:0;return i+=t||0,!e||i<=3},_lineSegmentsIntersectsRange:function(t,e,i,o){var a,n,s=this._getProjectedPoints();o=o||0;for(var r=i;r>o;r--)if(a=s[r-1],n=s[r],L.LineUtil.segmentsIntersect(t,e,a,n))return!0;return!1},_getProjectedPoints:function(){if(!this._defaultShape)return this._originalPoints;for(var t=[],e=this._defaultShape(),i=0;i=2?L.Toolbar.include(L.Evented.prototype):L.Toolbar.include(L.Mixin.Events)},enabled:function(){return null!==this._activeMode},disable:function(){this.enabled()&&this._activeMode.handler.disable()},addToolbar:function(t){var e,i=L.DomUtil.create("div","leaflet-draw-section"),o=0,a=this._toolbarClass||"",n=this.getModeHandlers(t);for(this._toolbarContainer=L.DomUtil.create("div","leaflet-draw-toolbar leaflet-bar"),this._map=t,e=0;e0&&this._singleLineLabel&&(L.DomUtil.removeClass(this._container,"leaflet-draw-tooltip-single"),this._singleLineLabel=!1):(L.DomUtil.addClass(this._container,"leaflet-draw-tooltip-single"),this._singleLineLabel=!0),this._container.innerHTML=(t.subtext.length>0?''+t.subtext+"
":"")+""+t.text+"",t.text||t.subtext?(this._visible=!0,this._container.style.visibility="inherit"):(this._visible=!1,this._container.style.visibility="hidden"),this):this},updatePosition:function(t){var e=this._map.latLngToLayerPoint(t),i=this._container;return this._container&&(this._visible&&(i.style.visibility="inherit"),L.DomUtil.setPosition(i,e)),this},showAsError:function(){return this._container&&L.DomUtil.addClass(this._container,"leaflet-error-draw-tooltip"),this},removeError:function(){return this._container&&L.DomUtil.removeClass(this._container,"leaflet-error-draw-tooltip"),this},_onMouseOut:function(){this._container&&(this._container.style.visibility="hidden")}}),L.DrawToolbar=L.Toolbar.extend({statics:{TYPE:"draw"},options:{polyline:{},polygon:{},rectangle:{},circle:{},marker:{},circlemarker:{}},initialize:function(t){for(var e in this.options)this.options.hasOwnProperty(e)&&t[e]&&(t[e]=L.extend({},this.options[e],t[e]));this._toolbarClass="leaflet-draw-draw",L.Toolbar.prototype.initialize.call(this,t)},getModeHandlers:function(t){return[{enabled:this.options.polyline,handler:new L.Draw.Polyline(t,this.options.polyline),title:L.drawLocal.draw.toolbar.buttons.polyline},{enabled:this.options.polygon,handler:new L.Draw.Polygon(t,this.options.polygon),title:L.drawLocal.draw.toolbar.buttons.polygon},{enabled:this.options.rectangle,handler:new L.Draw.Rectangle(t,this.options.rectangle),title:L.drawLocal.draw.toolbar.buttons.rectangle},{enabled:this.options.circle,handler:new L.Draw.Circle(t,this.options.circle),title:L.drawLocal.draw.toolbar.buttons.circle},{enabled:this.options.marker,handler:new L.Draw.Marker(t,this.options.marker),title:L.drawLocal.draw.toolbar.buttons.marker},{enabled:this.options.circlemarker,handler:new L.Draw.CircleMarker(t,this.options.circlemarker),title:L.drawLocal.draw.toolbar.buttons.circlemarker}]},getActions:function(t){return[{enabled:t.completeShape,title:L.drawLocal.draw.toolbar.finish.title,text:L.drawLocal.draw.toolbar.finish.text,callback:t.completeShape,context:t},{enabled:t.deleteLastVertex,title:L.drawLocal.draw.toolbar.undo.title,text:L.drawLocal.draw.toolbar.undo.text,callback:t.deleteLastVertex,context:t},{title:L.drawLocal.draw.toolbar.actions.title,text:L.drawLocal.draw.toolbar.actions.text,callback:this.disable,context:this}]},setOptions:function(t){L.setOptions(this,t);for(var e in this._modes)this._modes.hasOwnProperty(e)&&t.hasOwnProperty(e)&&this._modes[e].handler.setOptions(t[e])}}),L.EditToolbar=L.Toolbar.extend({statics:{TYPE:"edit"},options:{edit:{selectedPathOptions:{dashArray:"10, 10",fill:!0,fillColor:"#fe57a1",fillOpacity:.1,maintainColor:!1}},remove:{},poly:null,featureGroup:null},initialize:function(t){t.edit&&(void 0===t.edit.selectedPathOptions&&(t.edit.selectedPathOptions=this.options.edit.selectedPathOptions),t.edit.selectedPathOptions=L.extend({},this.options.edit.selectedPathOptions,t.edit.selectedPathOptions)),t.remove&&(t.remove=L.extend({},this.options.remove,t.remove)),t.poly&&(t.poly=L.extend({},this.options.poly,t.poly)),this._toolbarClass="leaflet-draw-edit",L.Toolbar.prototype.initialize.call(this,t),this._selectedFeatureCount=0},getModeHandlers:function(t){var e=this.options.featureGroup;return[{enabled:this.options.edit,handler:new L.EditToolbar.Edit(t,{featureGroup:e,selectedPathOptions:this.options.edit.selectedPathOptions,poly:this.options.poly}),title:L.drawLocal.edit.toolbar.buttons.edit},{enabled:this.options.remove,handler:new L.EditToolbar.Delete(t,{featureGroup:e}),title:L.drawLocal.edit.toolbar.buttons.remove}]},getActions:function(t){var e=[{title:L.drawLocal.edit.toolbar.actions.save.title,text:L.drawLocal.edit.toolbar.actions.save.text,callback:this._save,context:this},{title:L.drawLocal.edit.toolbar.actions.cancel.title,text:L.drawLocal.edit.toolbar.actions.cancel.text,callback:this.disable,context:this}];return t.removeAllLayers&&e.push({title:L.drawLocal.edit.toolbar.actions.clearAll.title,text:L.drawLocal.edit.toolbar.actions.clearAll.text,callback:this._clearAllLayers,context:this}),e},addToolbar:function(t){var e=L.Toolbar.prototype.addToolbar.call(this,t);return this._checkDisabled(),this.options.featureGroup.on("layeradd layerremove",this._checkDisabled,this),e},removeToolbar:function(){this.options.featureGroup.off("layeradd layerremove",this._checkDisabled,this),L.Toolbar.prototype.removeToolbar.call(this)},disable:function(){this.enabled()&&(this._activeMode.handler.revertLayers(),L.Toolbar.prototype.disable.call(this))},_save:function(){this._activeMode.handler.save(),this._activeMode&&this._activeMode.handler.disable()},_clearAllLayers:function(){this._activeMode.handler.removeAllLayers(),this._activeMode&&this._activeMode.handler.disable()},_checkDisabled:function(){var t,e=this.options.featureGroup,i=0!==e.getLayers().length;this.options.edit&&(t=this._modes[L.EditToolbar.Edit.TYPE].button,i?L.DomUtil.removeClass(t,"leaflet-disabled"):L.DomUtil.addClass(t,"leaflet-disabled"),t.setAttribute("title",i?L.drawLocal.edit.toolbar.buttons.edit:L.drawLocal.edit.toolbar.buttons.editDisabled)),this.options.remove&&(t=this._modes[L.EditToolbar.Delete.TYPE].button,i?L.DomUtil.removeClass(t,"leaflet-disabled"):L.DomUtil.addClass(t,"leaflet-disabled"),t.setAttribute("title",i?L.drawLocal.edit.toolbar.buttons.remove:L.drawLocal.edit.toolbar.buttons.removeDisabled))}}),L.EditToolbar.Edit=L.Handler.extend({statics:{TYPE:"edit"},initialize:function(t,e){if(L.Handler.prototype.initialize.call(this,t),L.setOptions(this,e),this._featureGroup=e.featureGroup,!(this._featureGroup instanceof L.FeatureGroup))throw new Error("options.featureGroup must be a L.FeatureGroup");this._uneditedLayerProps={},this.type=L.EditToolbar.Edit.TYPE;var i=L.version.split(".");1===parseInt(i[0],10)&&parseInt(i[1],10)>=2?L.EditToolbar.Edit.include(L.Evented.prototype):L.EditToolbar.Edit.include(L.Mixin.Events)},enable:function(){!this._enabled&&this._hasAvailableLayers()&&(this.fire("enabled",{handler:this.type}),this._map.fire(L.Draw.Event.EDITSTART,{handler:this.type}),L.Handler.prototype.enable.call(this),this._featureGroup.on("layeradd",this._enableLayerEdit,this).on("layerremove",this._disableLayerEdit,this))},disable:function(){this._enabled&&(this._featureGroup.off("layeradd",this._enableLayerEdit,this).off("layerremove",this._disableLayerEdit,this),L.Handler.prototype.disable.call(this),this._map.fire(L.Draw.Event.EDITSTOP,{handler:this.type}),this.fire("disabled",{handler:this.type}))},addHooks:function(){var t=this._map;t&&(t.getContainer().focus(),this._featureGroup.eachLayer(this._enableLayerEdit,this),this._tooltip=new L.Draw.Tooltip(this._map),this._tooltip.updateContent({text:L.drawLocal.edit.handlers.edit.tooltip.text,subtext:L.drawLocal.edit.handlers.edit.tooltip.subtext}),t._editTooltip=this._tooltip,this._updateTooltip(),this._map.on("mousemove",this._onMouseMove,this).on("touchmove",this._onMouseMove,this).on("MSPointerMove",this._onMouseMove,this).on(L.Draw.Event.EDITVERTEX,this._updateTooltip,this))},removeHooks:function(){this._map&&(this._featureGroup.eachLayer(this._disableLayerEdit,this),this._uneditedLayerProps={},this._tooltip.dispose(),this._tooltip=null,this._map.off("mousemove",this._onMouseMove,this).off("touchmove",this._onMouseMove,this).off("MSPointerMove",this._onMouseMove,this).off(L.Draw.Event.EDITVERTEX,this._updateTooltip,this))},revertLayers:function(){this._featureGroup.eachLayer(function(t){this._revertLayer(t)},this)},save:function(){var t=new L.LayerGroup;this._featureGroup.eachLayer(function(e){e.edited&&(t.addLayer(e),e.edited=!1)}),this._map.fire(L.Draw.Event.EDITED,{layers:t})},_backupLayer:function(t){var e=L.Util.stamp(t);this._uneditedLayerProps[e]||(t instanceof L.Polyline||t instanceof L.Polygon||t instanceof L.Rectangle?this._uneditedLayerProps[e]={latlngs:L.LatLngUtil.cloneLatLngs(t.getLatLngs())}:t instanceof L.Circle?this._uneditedLayerProps[e]={latlng:L.LatLngUtil.cloneLatLng(t.getLatLng()),radius:t.getRadius()}:(t instanceof L.Marker||t instanceof L.CircleMarker)&&(this._uneditedLayerProps[e]={latlng:L.LatLngUtil.cloneLatLng(t.getLatLng())}))},_getTooltipText:function(){return{text:L.drawLocal.edit.handlers.edit.tooltip.text,subtext:L.drawLocal.edit.handlers.edit.tooltip.subtext}},_updateTooltip:function(){this._tooltip.updateContent(this._getTooltipText())},_revertLayer:function(t){var e=L.Util.stamp(t);t.edited=!1,this._uneditedLayerProps.hasOwnProperty(e)&&(t instanceof L.Polyline||t instanceof L.Polygon||t instanceof L.Rectangle?t.setLatLngs(this._uneditedLayerProps[e].latlngs):t instanceof L.Circle?(t.setLatLng(this._uneditedLayerProps[e].latlng),t.setRadius(this._uneditedLayerProps[e].radius)):(t instanceof L.Marker||t instanceof L.CircleMarker)&&t.setLatLng(this._uneditedLayerProps[e].latlng),t.fire("revert-edited",{layer:t}))},_enableLayerEdit:function(t){var e,i,o=t.layer||t.target||t;this._backupLayer(o),this.options.poly&&(i=L.Util.extend({},this.options.poly),o.options.poly=i),this.options.selectedPathOptions&&(e=L.Util.extend({},this.options.selectedPathOptions),e.maintainColor&&(e.color=o.options.color,e.fillColor=o.options.fillColor),o.options.original=L.extend({},o.options),o.options.editing=e),o instanceof L.Marker?(o.editing&&o.editing.enable(),o.dragging.enable(),o.on("dragend",this._onMarkerDragEnd).on("touchmove",this._onTouchMove,this).on("MSPointerMove",this._onTouchMove,this).on("touchend",this._onMarkerDragEnd,this).on("MSPointerUp",this._onMarkerDragEnd,this)):o.editing.enable()},_disableLayerEdit:function(t){var e=t.layer||t.target||t;e.edited=!1,e.editing&&e.editing.disable(),delete e.options.editing,delete e.options.original, 10 | this._selectedPathOptions&&(e instanceof L.Marker?this._toggleMarkerHighlight(e):(e.setStyle(e.options.previousOptions),delete e.options.previousOptions)),e instanceof L.Marker?(e.dragging.disable(),e.off("dragend",this._onMarkerDragEnd,this).off("touchmove",this._onTouchMove,this).off("MSPointerMove",this._onTouchMove,this).off("touchend",this._onMarkerDragEnd,this).off("MSPointerUp",this._onMarkerDragEnd,this)):e.editing.disable()},_onMouseMove:function(t){this._tooltip.updatePosition(t.latlng)},_onMarkerDragEnd:function(t){var e=t.target;e.edited=!0,this._map.fire(L.Draw.Event.EDITMOVE,{layer:e})},_onTouchMove:function(t){var e=t.originalEvent.changedTouches[0],i=this._map.mouseEventToLayerPoint(e),o=this._map.layerPointToLatLng(i);t.target.setLatLng(o)},_hasAvailableLayers:function(){return 0!==this._featureGroup.getLayers().length}}),L.EditToolbar.Delete=L.Handler.extend({statics:{TYPE:"remove"},initialize:function(t,e){if(L.Handler.prototype.initialize.call(this,t),L.Util.setOptions(this,e),this._deletableLayers=this.options.featureGroup,!(this._deletableLayers instanceof L.FeatureGroup))throw new Error("options.featureGroup must be a L.FeatureGroup");this.type=L.EditToolbar.Delete.TYPE;var i=L.version.split(".");1===parseInt(i[0],10)&&parseInt(i[1],10)>=2?L.EditToolbar.Delete.include(L.Evented.prototype):L.EditToolbar.Delete.include(L.Mixin.Events)},enable:function(){!this._enabled&&this._hasAvailableLayers()&&(this.fire("enabled",{handler:this.type}),this._map.fire(L.Draw.Event.DELETESTART,{handler:this.type}),L.Handler.prototype.enable.call(this),this._deletableLayers.on("layeradd",this._enableLayerDelete,this).on("layerremove",this._disableLayerDelete,this))},disable:function(){this._enabled&&(this._deletableLayers.off("layeradd",this._enableLayerDelete,this).off("layerremove",this._disableLayerDelete,this),L.Handler.prototype.disable.call(this),this._map.fire(L.Draw.Event.DELETESTOP,{handler:this.type}),this.fire("disabled",{handler:this.type}))},addHooks:function(){var t=this._map;t&&(t.getContainer().focus(),this._deletableLayers.eachLayer(this._enableLayerDelete,this),this._deletedLayers=new L.LayerGroup,this._tooltip=new L.Draw.Tooltip(this._map),this._tooltip.updateContent({text:L.drawLocal.edit.handlers.remove.tooltip.text}),this._map.on("mousemove",this._onMouseMove,this))},removeHooks:function(){this._map&&(this._deletableLayers.eachLayer(this._disableLayerDelete,this),this._deletedLayers=null,this._tooltip.dispose(),this._tooltip=null,this._map.off("mousemove",this._onMouseMove,this))},revertLayers:function(){this._deletedLayers.eachLayer(function(t){this._deletableLayers.addLayer(t),t.fire("revert-deleted",{layer:t})},this)},save:function(){this._map.fire(L.Draw.Event.DELETED,{layers:this._deletedLayers})},removeAllLayers:function(){this._deletableLayers.eachLayer(function(t){this._removeLayer({layer:t})},this),this.save()},_enableLayerDelete:function(t){(t.layer||t.target||t).on("click",this._removeLayer,this)},_disableLayerDelete:function(t){var e=t.layer||t.target||t;e.off("click",this._removeLayer,this),this._deletedLayers.removeLayer(e)},_removeLayer:function(t){var e=t.layer||t.target||t;this._deletableLayers.removeLayer(e),this._deletedLayers.addLayer(e),e.fire("deleted")},_onMouseMove:function(t){this._tooltip.updatePosition(t.latlng)},_hasAvailableLayers:function(){return 0!==this._deletableLayers.getLayers().length}})}(window,document); -------------------------------------------------------------------------------- /public_html/vendor/leaflet.fullscreen/Control.FullScreen.css: -------------------------------------------------------------------------------- 1 | .leaflet-control-zoom-fullscreen { background-image: url(images/icon-fullscreen.png); } 2 | .leaflet-retina .leaflet-control-zoom-fullscreen { background-image: url(images/icon-fullscreen-2x.png); background-size: 26px 26px; } 3 | .leaflet-container:-webkit-full-screen { width: 100% !important; height: 100% !important; z-index: 99999; } 4 | .leaflet-pseudo-fullscreen { position: fixed !important; width: 100% !important; height: 100% !important; top: 0px !important; left: 0px !important; z-index: 99999; } 5 | -------------------------------------------------------------------------------- /public_html/vendor/leaflet.fullscreen/Control.FullScreen.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | L.Control.FullScreen = L.Control.extend({ 4 | options: { 5 | position: 'topleft', 6 | title: 'Full Screen', 7 | forceSeparateButton: false, 8 | forcePseudoFullscreen: false 9 | }, 10 | 11 | onAdd: function (map) { 12 | var className = 'leaflet-control-zoom-fullscreen', container; 13 | 14 | if (map.zoomControl && !this.options.forceSeparateButton) { 15 | container = map.zoomControl._container; 16 | } else { 17 | container = L.DomUtil.create('div', 'leaflet-bar'); 18 | } 19 | 20 | this._createButton(this.options.title, className, container, this.toggleFullScreen, this); 21 | 22 | return container; 23 | }, 24 | 25 | _createButton: function (title, className, container, fn, context) { 26 | var link = L.DomUtil.create('a', className, container); 27 | link.href = '#'; 28 | link.title = title; 29 | 30 | L.DomEvent 31 | .addListener(link, 'click', L.DomEvent.stopPropagation) 32 | .addListener(link, 'click', L.DomEvent.preventDefault) 33 | .addListener(link, 'click', fn, context); 34 | 35 | L.DomEvent 36 | .addListener(container, fullScreenApi.fullScreenEventName, L.DomEvent.stopPropagation) 37 | .addListener(container, fullScreenApi.fullScreenEventName, L.DomEvent.preventDefault) 38 | .addListener(container, fullScreenApi.fullScreenEventName, this._handleEscKey, context); 39 | 40 | L.DomEvent 41 | .addListener(document, fullScreenApi.fullScreenEventName, L.DomEvent.stopPropagation) 42 | .addListener(document, fullScreenApi.fullScreenEventName, L.DomEvent.preventDefault) 43 | .addListener(document, fullScreenApi.fullScreenEventName, this._handleEscKey, context); 44 | 45 | return link; 46 | }, 47 | 48 | toggleFullScreen: function () { 49 | var map = this._map; 50 | map._exitFired = false; 51 | if (map._isFullscreen) { 52 | if (fullScreenApi.supportsFullScreen && !this.options.forcePseudoFullscreen) { 53 | fullScreenApi.cancelFullScreen(map._container); 54 | } else { 55 | L.DomUtil.removeClass(map._container, 'leaflet-pseudo-fullscreen'); 56 | } 57 | map.invalidateSize(); 58 | map.fire('exitFullscreen'); 59 | map._exitFired = true; 60 | map._isFullscreen = false; 61 | } 62 | else { 63 | if (fullScreenApi.supportsFullScreen && !this.options.forcePseudoFullscreen) { 64 | fullScreenApi.requestFullScreen(map._container); 65 | } else { 66 | L.DomUtil.addClass(map._container, 'leaflet-pseudo-fullscreen'); 67 | } 68 | map.invalidateSize(); 69 | map.fire('enterFullscreen'); 70 | map._isFullscreen = true; 71 | } 72 | }, 73 | 74 | _handleEscKey: function () { 75 | var map = this._map; 76 | if (!fullScreenApi.isFullScreen(map) && !map._exitFired) { 77 | map.fire('exitFullscreen'); 78 | map._exitFired = true; 79 | map._isFullscreen = false; 80 | } 81 | } 82 | }); 83 | 84 | L.Map.addInitHook(function () { 85 | if (this.options.fullscreenControl) { 86 | this.fullscreenControl = L.control.fullscreen(this.options.fullscreenControlOptions); 87 | this.addControl(this.fullscreenControl); 88 | } 89 | }); 90 | 91 | L.control.fullscreen = function (options) { 92 | return new L.Control.FullScreen(options); 93 | }; 94 | 95 | /* 96 | Native FullScreen JavaScript API 97 | ------------- 98 | Assumes Mozilla naming conventions instead of W3C for now 99 | 100 | source : http://johndyer.name/native-fullscreen-javascript-api-plus-jquery-plugin/ 101 | 102 | */ 103 | 104 | var 105 | fullScreenApi = { 106 | supportsFullScreen: false, 107 | isFullScreen: function() { return false; }, 108 | requestFullScreen: function() {}, 109 | cancelFullScreen: function() {}, 110 | fullScreenEventName: '', 111 | prefix: '' 112 | }, 113 | browserPrefixes = 'webkit moz o ms khtml'.split(' '); 114 | 115 | // check for native support 116 | if (typeof document.exitFullscreen !== 'undefined') { 117 | fullScreenApi.supportsFullScreen = true; 118 | } else { 119 | // check for fullscreen support by vendor prefix 120 | for (var i = 0, il = browserPrefixes.length; i < il; i++ ) { 121 | fullScreenApi.prefix = browserPrefixes[i]; 122 | if (typeof document[fullScreenApi.prefix + 'CancelFullScreen' ] !== 'undefined' ) { 123 | fullScreenApi.supportsFullScreen = true; 124 | break; 125 | } 126 | } 127 | } 128 | 129 | // update methods to do something useful 130 | if (fullScreenApi.supportsFullScreen) { 131 | fullScreenApi.fullScreenEventName = fullScreenApi.prefix + 'fullscreenchange'; 132 | fullScreenApi.isFullScreen = function() { 133 | switch (this.prefix) { 134 | case '': 135 | return document.fullScreen; 136 | case 'webkit': 137 | return document.webkitIsFullScreen; 138 | default: 139 | return document[this.prefix + 'FullScreen']; 140 | } 141 | }; 142 | fullScreenApi.requestFullScreen = function(el) { 143 | return (this.prefix === '') ? el.requestFullscreen() : el[this.prefix + 'RequestFullScreen'](); 144 | }; 145 | fullScreenApi.cancelFullScreen = function(el) { 146 | return (this.prefix === '') ? document.exitFullscreen() : document[this.prefix + 'CancelFullScreen'](); 147 | }; 148 | } 149 | 150 | // jQuery plugin 151 | if (typeof jQuery !== 'undefined') { 152 | jQuery.fn.requestFullScreen = function() { 153 | return this.each(function() { 154 | var el = jQuery(this); 155 | if (fullScreenApi.supportsFullScreen) { 156 | fullScreenApi.requestFullScreen(el); 157 | } 158 | }); 159 | }; 160 | } 161 | 162 | // export api 163 | window.fullScreenApi = fullScreenApi; 164 | })(); 165 | -------------------------------------------------------------------------------- /public_html/vendor/leaflet.fullscreen/images/icon-fullscreen-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitequark/groupXIV/d2140be3acbdd20286f9eddc5ec10b8db67b817a/public_html/vendor/leaflet.fullscreen/images/icon-fullscreen-2x.png -------------------------------------------------------------------------------- /public_html/vendor/leaflet.fullscreen/images/icon-fullscreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitequark/groupXIV/d2140be3acbdd20286f9eddc5ec10b8db67b817a/public_html/vendor/leaflet.fullscreen/images/icon-fullscreen.png -------------------------------------------------------------------------------- /public_html/vendor/leaflet.loading/Control.Loading.css: -------------------------------------------------------------------------------- 1 | .leaflet-control-loading:empty { 2 | /* Spinner via ajaxload.info, base64-encoded */ 3 | background-image: url(data:image/gif;base64,R0lGODlhEAAQAPQAAP///wAAAPDw8IqKiuDg4EZGRnp6egAAAFhYWCQkJKysrL6+vhQUFJycnAQEBDY2NmhoaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH+GkNyZWF0ZWQgd2l0aCBhamF4bG9hZC5pbmZvACH5BAAKAAAAIf8LTkVUU0NBUEUyLjADAQAAACwAAAAAEAAQAAAFdyAgAgIJIeWoAkRCCMdBkKtIHIngyMKsErPBYbADpkSCwhDmQCBethRB6Vj4kFCkQPG4IlWDgrNRIwnO4UKBXDufzQvDMaoSDBgFb886MiQadgNABAokfCwzBA8LCg0Egl8jAggGAA1kBIA1BAYzlyILczULC2UhACH5BAAKAAEALAAAAAAQABAAAAV2ICACAmlAZTmOREEIyUEQjLKKxPHADhEvqxlgcGgkGI1DYSVAIAWMx+lwSKkICJ0QsHi9RgKBwnVTiRQQgwF4I4UFDQQEwi6/3YSGWRRmjhEETAJfIgMFCnAKM0KDV4EEEAQLiF18TAYNXDaSe3x6mjidN1s3IQAh+QQACgACACwAAAAAEAAQAAAFeCAgAgLZDGU5jgRECEUiCI+yioSDwDJyLKsXoHFQxBSHAoAAFBhqtMJg8DgQBgfrEsJAEAg4YhZIEiwgKtHiMBgtpg3wbUZXGO7kOb1MUKRFMysCChAoggJCIg0GC2aNe4gqQldfL4l/Ag1AXySJgn5LcoE3QXI3IQAh+QQACgADACwAAAAAEAAQAAAFdiAgAgLZNGU5joQhCEjxIssqEo8bC9BRjy9Ag7GILQ4QEoE0gBAEBcOpcBA0DoxSK/e8LRIHn+i1cK0IyKdg0VAoljYIg+GgnRrwVS/8IAkICyosBIQpBAMoKy9dImxPhS+GKkFrkX+TigtLlIyKXUF+NjagNiEAIfkEAAoABAAsAAAAABAAEAAABWwgIAICaRhlOY4EIgjH8R7LKhKHGwsMvb4AAy3WODBIBBKCsYA9TjuhDNDKEVSERezQEL0WrhXucRUQGuik7bFlngzqVW9LMl9XWvLdjFaJtDFqZ1cEZUB0dUgvL3dgP4WJZn4jkomWNpSTIyEAIfkEAAoABQAsAAAAABAAEAAABX4gIAICuSxlOY6CIgiD8RrEKgqGOwxwUrMlAoSwIzAGpJpgoSDAGifDY5kopBYDlEpAQBwevxfBtRIUGi8xwWkDNBCIwmC9Vq0aiQQDQuK+VgQPDXV9hCJjBwcFYU5pLwwHXQcMKSmNLQcIAExlbH8JBwttaX0ABAcNbWVbKyEAIfkEAAoABgAsAAAAABAAEAAABXkgIAICSRBlOY7CIghN8zbEKsKoIjdFzZaEgUBHKChMJtRwcWpAWoWnifm6ESAMhO8lQK0EEAV3rFopIBCEcGwDKAqPh4HUrY4ICHH1dSoTFgcHUiZjBhAJB2AHDykpKAwHAwdzf19KkASIPl9cDgcnDkdtNwiMJCshACH5BAAKAAcALAAAAAAQABAAAAV3ICACAkkQZTmOAiosiyAoxCq+KPxCNVsSMRgBsiClWrLTSWFoIQZHl6pleBh6suxKMIhlvzbAwkBWfFWrBQTxNLq2RG2yhSUkDs2b63AYDAoJXAcFRwADeAkJDX0AQCsEfAQMDAIPBz0rCgcxky0JRWE1AmwpKyEAIfkEAAoACAAsAAAAABAAEAAABXkgIAICKZzkqJ4nQZxLqZKv4NqNLKK2/Q4Ek4lFXChsg5ypJjs1II3gEDUSRInEGYAw6B6zM4JhrDAtEosVkLUtHA7RHaHAGJQEjsODcEg0FBAFVgkQJQ1pAwcDDw8KcFtSInwJAowCCA6RIwqZAgkPNgVpWndjdyohACH5BAAKAAkALAAAAAAQABAAAAV5ICACAimc5KieLEuUKvm2xAKLqDCfC2GaO9eL0LABWTiBYmA06W6kHgvCqEJiAIJiu3gcvgUsscHUERm+kaCxyxa+zRPk0SgJEgfIvbAdIAQLCAYlCj4DBw0IBQsMCjIqBAcPAooCBg9pKgsJLwUFOhCZKyQDA3YqIQAh+QQACgAKACwAAAAAEAAQAAAFdSAgAgIpnOSonmxbqiThCrJKEHFbo8JxDDOZYFFb+A41E4H4OhkOipXwBElYITDAckFEOBgMQ3arkMkUBdxIUGZpEb7kaQBRlASPg0FQQHAbEEMGDSVEAA1QBhAED1E0NgwFAooCDWljaQIQCE5qMHcNhCkjIQAh+QQACgALACwAAAAAEAAQAAAFeSAgAgIpnOSoLgxxvqgKLEcCC65KEAByKK8cSpA4DAiHQ/DkKhGKh4ZCtCyZGo6F6iYYPAqFgYy02xkSaLEMV34tELyRYNEsCQyHlvWkGCzsPgMCEAY7Cg04Uk48LAsDhRA8MVQPEF0GAgqYYwSRlycNcWskCkApIyEAOwAAAAAAAAAAAA==); 4 | background-repeat: no-repeat; 5 | } 6 | 7 | .leaflet-control-loading, 8 | .leaflet-control-zoom a.leaflet-control-loading , 9 | .leaflet-control-zoomslider a.leaflet-control-loading { 10 | display: none; 11 | } 12 | 13 | .leaflet-control-loading.is-loading, 14 | .leaflet-control-zoom a.leaflet-control-loading.is-loading, 15 | .leaflet-control-zoomslider a.leaflet-control-loading.is-loading { 16 | display: block; 17 | } 18 | 19 | /* Necessary for display consistency in Leaflet >= 0.6 */ 20 | .leaflet-bar-part-bottom { 21 | border-bottom: medium none; 22 | border-bottom-left-radius: 4px; 23 | border-bottom-right-radius: 4px; 24 | } 25 | -------------------------------------------------------------------------------- /public_html/vendor/leaflet.loading/Control.Loading.js: -------------------------------------------------------------------------------- 1 | /* 2 | * L.Control.Loading is a control that shows a loading indicator when tiles are 3 | * loading or when map-related AJAX requests are taking place. 4 | */ 5 | 6 | (function () { 7 | 8 | var console = window.console || { 9 | error: function () {}, 10 | warn: function () {} 11 | }; 12 | 13 | function defineLeafletLoading(L) { 14 | L.Control.Loading = L.Control.extend({ 15 | options: { 16 | position: 'topleft', 17 | separate: false, 18 | zoomControl: null, 19 | spinjs: false, 20 | spin: { 21 | lines: 7, 22 | length: 3, 23 | width: 3, 24 | radius: 5, 25 | rotate: 13, 26 | top: "83%" 27 | } 28 | }, 29 | 30 | initialize: function(options) { 31 | L.setOptions(this, options); 32 | this._dataLoaders = {}; 33 | 34 | // Try to set the zoom control this control is attached to from the 35 | // options 36 | if (this.options.zoomControl !== null) { 37 | this.zoomControl = this.options.zoomControl; 38 | } 39 | }, 40 | 41 | onAdd: function(map) { 42 | if (this.options.spinjs && (typeof Spinner !== 'function')) { 43 | return console.error("Leaflet.loading cannot load because you didn't load spin.js (http://fgnass.github.io/spin.js/), even though you set it in options."); 44 | } 45 | this._addLayerListeners(map); 46 | this._addMapListeners(map); 47 | 48 | // Try to set the zoom control this control is attached to from the map 49 | // the control is being added to 50 | if (!this.options.separate && !this.zoomControl) { 51 | if (map.zoomControl) { 52 | this.zoomControl = map.zoomControl; 53 | } else if (map.zoomsliderControl) { 54 | this.zoomControl = map.zoomsliderControl; 55 | } 56 | } 57 | 58 | // Create the loading indicator 59 | var classes = 'leaflet-control-loading'; 60 | var container; 61 | if (this.zoomControl && !this.options.separate) { 62 | // If there is a zoom control, hook into the bottom of it 63 | container = this.zoomControl._container; 64 | // These classes are no longer used as of Leaflet 0.6 65 | classes += ' leaflet-bar-part-bottom leaflet-bar-part last'; 66 | 67 | // Loading control will be added to the zoom control. So the visible last element is not the 68 | // last dom element anymore. So add the part-bottom class. 69 | L.DomUtil.addClass(this._getLastControlButton(), 'leaflet-bar-part-bottom'); 70 | } 71 | else { 72 | // Otherwise, create a container for the indicator 73 | container = L.DomUtil.create('div', 'leaflet-control-zoom leaflet-bar'); 74 | } 75 | this._indicator = L.DomUtil.create('a', classes, container); 76 | if (this.options.spinjs) { 77 | this._spinner = new Spinner(this.options.spin).spin(); 78 | this._indicator.appendChild(this._spinner.el); 79 | } 80 | return container; 81 | }, 82 | 83 | onRemove: function(map) { 84 | this._removeLayerListeners(map); 85 | this._removeMapListeners(map); 86 | }, 87 | 88 | removeFrom: function (map) { 89 | if (this.zoomControl && !this.options.separate) { 90 | // Override Control.removeFrom() to avoid clobbering the entire 91 | // _container, which is the same as zoomControl's 92 | this._container.removeChild(this._indicator); 93 | this._map = null; 94 | this.onRemove(map); 95 | return this; 96 | } 97 | else { 98 | // If this control is separate from the zoomControl, call the 99 | // parent method so we don't leave behind an empty container 100 | return L.Control.prototype.removeFrom.call(this, map); 101 | } 102 | }, 103 | 104 | addLoader: function(id) { 105 | this._dataLoaders[id] = true; 106 | this.updateIndicator(); 107 | }, 108 | 109 | removeLoader: function(id) { 110 | delete this._dataLoaders[id]; 111 | this.updateIndicator(); 112 | }, 113 | 114 | updateIndicator: function() { 115 | if (this.isLoading()) { 116 | this._showIndicator(); 117 | } 118 | else { 119 | this._hideIndicator(); 120 | } 121 | }, 122 | 123 | isLoading: function() { 124 | return this._countLoaders() > 0; 125 | }, 126 | 127 | _countLoaders: function() { 128 | var size = 0, key; 129 | for (key in this._dataLoaders) { 130 | if (this._dataLoaders.hasOwnProperty(key)) size++; 131 | } 132 | return size; 133 | }, 134 | 135 | _showIndicator: function() { 136 | // Show loading indicator 137 | L.DomUtil.addClass(this._indicator, 'is-loading'); 138 | 139 | // If zoomControl exists, make the zoom-out button not last 140 | if (!this.options.separate) { 141 | if (this.zoomControl instanceof L.Control.Zoom) { 142 | L.DomUtil.removeClass(this._getLastControlButton(), 'leaflet-bar-part-bottom'); 143 | } 144 | else if (typeof L.Control.Zoomslider === 'function' && this.zoomControl instanceof L.Control.Zoomslider) { 145 | L.DomUtil.removeClass(this.zoomControl._ui.zoomOut, 'leaflet-bar-part-bottom'); 146 | } 147 | } 148 | }, 149 | 150 | _hideIndicator: function() { 151 | // Hide loading indicator 152 | L.DomUtil.removeClass(this._indicator, 'is-loading'); 153 | 154 | // If zoomControl exists, make the zoom-out button last 155 | if (!this.options.separate) { 156 | if (this.zoomControl instanceof L.Control.Zoom) { 157 | L.DomUtil.addClass(this._getLastControlButton(), 'leaflet-bar-part-bottom'); 158 | } 159 | else if (typeof L.Control.Zoomslider === 'function' && this.zoomControl instanceof L.Control.Zoomslider) { 160 | L.DomUtil.addClass(this.zoomControl._ui.zoomOut, 'leaflet-bar-part-bottom'); 161 | } 162 | } 163 | }, 164 | 165 | _getLastControlButton: function() { 166 | var container = this.zoomControl._container, 167 | index = container.children.length - 1; 168 | 169 | // Find the last visible control button that is not our loading 170 | // indicator 171 | while (index > 0) { 172 | var button = container.children[index]; 173 | if (!(this._indicator === button || button.offsetWidth === 0 || button.offsetHeight === 0)) { 174 | break; 175 | } 176 | index--; 177 | } 178 | 179 | return container.children[index]; 180 | }, 181 | 182 | _handleLoading: function(e) { 183 | this.addLoader(this.getEventId(e)); 184 | }, 185 | 186 | _handleLoad: function(e) { 187 | this.removeLoader(this.getEventId(e)); 188 | }, 189 | 190 | getEventId: function(e) { 191 | if (e.id) { 192 | return e.id; 193 | } 194 | else if (e.layer) { 195 | return e.layer._leaflet_id; 196 | } 197 | return e.target._leaflet_id; 198 | }, 199 | 200 | _layerAdd: function(e) { 201 | if (!e.layer || !e.layer.on) return 202 | try { 203 | e.layer.on({ 204 | loading: this._handleLoading, 205 | load: this._handleLoad 206 | }, this); 207 | } 208 | catch (exception) { 209 | console.warn('L.Control.Loading: Tried and failed to add ' + 210 | ' event handlers to layer', e.layer); 211 | console.warn('L.Control.Loading: Full details', exception); 212 | } 213 | }, 214 | 215 | _addLayerListeners: function(map) { 216 | // Add listeners for begin and end of load to any layers already on the 217 | // map 218 | map.eachLayer(function(layer) { 219 | if (!layer.on) return; 220 | layer.on({ 221 | loading: this._handleLoading, 222 | load: this._handleLoad 223 | }, this); 224 | }, this); 225 | 226 | // When a layer is added to the map, add listeners for begin and end 227 | // of load 228 | map.on('layeradd', this._layerAdd, this); 229 | }, 230 | 231 | _removeLayerListeners: function(map) { 232 | // Remove listeners for begin and end of load from all layers 233 | map.eachLayer(function(layer) { 234 | if (!layer.off) return; 235 | layer.off({ 236 | loading: this._handleLoading, 237 | load: this._handleLoad 238 | }, this); 239 | }, this); 240 | 241 | // Remove layeradd listener from map 242 | map.off('layeradd', this._layerAdd, this); 243 | }, 244 | 245 | _addMapListeners: function(map) { 246 | // Add listeners to the map for (custom) dataloading and dataload 247 | // events, eg, for AJAX calls that affect the map but will not be 248 | // reflected in the above layer events. 249 | map.on({ 250 | dataloading: this._handleLoading, 251 | dataload: this._handleLoad, 252 | layerremove: this._handleLoad 253 | }, this); 254 | }, 255 | 256 | _removeMapListeners: function(map) { 257 | map.off({ 258 | dataloading: this._handleLoading, 259 | dataload: this._handleLoad, 260 | layerremove: this._handleLoad 261 | }, this); 262 | } 263 | }); 264 | 265 | L.Map.addInitHook(function () { 266 | if (this.options.loadingControl) { 267 | this.loadingControl = new L.Control.Loading(); 268 | this.addControl(this.loadingControl); 269 | } 270 | }); 271 | 272 | L.Control.loading = function(options) { 273 | return new L.Control.Loading(options); 274 | }; 275 | } 276 | 277 | if (typeof define === 'function' && define.amd) { 278 | // Try to add leaflet.loading to Leaflet using AMD 279 | define(['leaflet'], function (L) { 280 | defineLeafletLoading(L); 281 | }); 282 | } 283 | else { 284 | // Else use the global L 285 | defineLeafletLoading(L); 286 | } 287 | 288 | })(); 289 | -------------------------------------------------------------------------------- /public_html/vendor/leaflet.nanomeasure/Control.Nanomeasure.css: -------------------------------------------------------------------------------- 1 | .leaflet-control-nanomeasure-coordinates { 2 | background-image: url(images/measure-coordinates-2x.png); 3 | } 4 | 5 | .leaflet-control-nanomeasure-distance { 6 | background-image: url(images/measure-distance-2x.png); 7 | } 8 | 9 | .leaflet-control-nanomeasure-area { 10 | background-image: url(images/measure-area-2x.png); 11 | } 12 | 13 | .leaflet-control-nanomeasure.enabled { 14 | background-color: #ffe0e0; 15 | } 16 | 17 | .leaflet-control-nanomeasure.enabled:hover { 18 | background-color: #ffd0d0; 19 | } 20 | -------------------------------------------------------------------------------- /public_html/vendor/leaflet.nanomeasure/Control.Nanomeasure.js: -------------------------------------------------------------------------------- 1 | L.Nanomeasure = {}; 2 | 3 | L.Nanomeasure.Util = { 4 | readableDistance: function(nanometers) { 5 | var absNanometers = Math.abs(nanometers); 6 | return absNanometers < 1000 ? nanometers.toFixed(0) + ' nm' : 7 | absNanometers < 1000000 ? (nanometers / 1000).toFixed(3) + ' \u00b5m' : 8 | (nanometers / 1000000).toFixed(3) + ' mm'; 9 | }, 10 | 11 | readableArea: function(nanometers) { 12 | var absNanometers = Math.abs(nanometers); 13 | return absNanometers < 1000*1000 ? nanometers.toFixed(0) + ' nm\u00b2' : 14 | absNanometers < 1000000*1000000 ? 15 | (nanometers / (1000*1000)).toFixed(0) + ' \u00b5m\u00b2' : 16 | (nanometers / (1000000*1000000)).toFixed(3) + ' mm\u00b2'; 17 | }, 18 | 19 | polygonArea: function(points) { 20 | var area = 0, j = points.length - 1; 21 | for(var i = 0; i < points.length; i++) { 22 | area += (points[j].x + points[i].x) * (points[j].y - points[i].y); 23 | j = i; 24 | } 25 | return Math.abs(area / 2); 26 | } 27 | } 28 | 29 | L.Nanomeasure.Coordinates = L.Draw.Marker. 30 | extend({ 31 | addHooks: function() { 32 | L.Draw.Marker.prototype.addHooks.call(this); 33 | 34 | if (this._map) { 35 | this._drawing = true; 36 | this._map.on('zoomstart', this._onZoomStart, this); 37 | this._map.on('zoomend', this._onZoomEnd, this); 38 | } 39 | }, 40 | 41 | removeHooks: function() { 42 | this._drawing = false; 43 | this._map.off('zoomstart', this._onZoomStart, this); 44 | this._map.off('zoomend', this._onZoomEnd, this); 45 | 46 | L.Draw.Marker.prototype.removeHooks.call(this); 47 | }, 48 | 49 | _onMouseMove: function(e) { 50 | if(this._drawing) { 51 | L.Draw.Marker.prototype._onMouseMove.call(this, e); 52 | 53 | var zoom = this.options.ratioAtZoom !== undefined ? 54 | this.options.ratioAtZoom : this._map.getMaxZoom(); 55 | var centerCoords = this._map.project(this._map.getCenter(), zoom); 56 | var markerCoords = this._map.project(this._mouseMarker.getLatLng(), zoom) 57 | var coords = markerCoords.subtract(centerCoords); 58 | 59 | this._tooltip.updateContent({ 60 | text: L.Nanomeasure.Util.readableDistance(coords.x) + ' ' + 61 | L.Nanomeasure.Util.readableDistance(coords.y) 62 | }); 63 | } 64 | }, 65 | 66 | _onClick: function() { 67 | if(this._drawing) { 68 | this._drawing = false; 69 | } else { 70 | this.disable(); 71 | if (this.options.repeatMode) { 72 | this.enable(); 73 | } 74 | this._drawing = true; 75 | } 76 | }, 77 | 78 | _onZoomStart: function(e) { 79 | this._tooltip._container.style.visibility = 'hidden'; 80 | }, 81 | 82 | _onZoomEnd: function(e) { 83 | this._tooltip.updatePosition(this._mouseMarker.getLatLng()); 84 | 85 | this._tooltip._container.style.visibility = 'inherit'; 86 | }, 87 | }); 88 | 89 | L.Nanomeasure.PolylineMixin = function(parent) { 90 | return { 91 | addHooks: function() { 92 | parent.prototype.addHooks.call(this); 93 | 94 | if (this._map) { 95 | this._map.on('click', this._onClick, this); 96 | this._map.on('zoomstart', this._onZoomStart, this); 97 | this._drawing = true; 98 | } 99 | }, 100 | 101 | removeHooks: function() { 102 | this._finishShape(); 103 | this._map.off('zoomstart', this._onZoomStart, this); 104 | this._map.off('click', this._onClick, this); 105 | 106 | parent.prototype.removeHooks.call(this); 107 | }, 108 | 109 | _startShape: function() { 110 | this._drawing = true; 111 | this._poly = new L.Polyline([], this.options.shapeOptions); 112 | 113 | this._container.style.cursor = 'crosshair'; 114 | 115 | this._updateTooltip(); 116 | this._map.on('mousemove', this._onMouseMove, this); 117 | }, 118 | 119 | _finishShape: function() { 120 | this._drawing = false; 121 | 122 | this._cleanUpShape(); 123 | this._clearGuides(); 124 | 125 | this._finalize(); 126 | this._updateTooltip(this._currentLatLng); 127 | 128 | this._map.off('mousemove', this._onMouseMove, this); 129 | this._container.style.cursor = ''; 130 | }, 131 | 132 | _removeShape: function() { 133 | this._markers.splice(0); 134 | this._markerGroup.clearLayers(); 135 | 136 | if (!this._poly) 137 | return; 138 | this._map.removeLayer(this._poly); 139 | delete this._poly; 140 | }, 141 | 142 | _onClick: function(e) { 143 | if (!this._drawing) { 144 | this._removeShape(); 145 | this._startShape(); 146 | } 147 | }, 148 | 149 | _onZoomStart: function(e) { 150 | this._tooltip._container.style.visibility = 'hidden'; 151 | }, 152 | 153 | _onZoomEnd: function(e) { 154 | if(this._drawing) { 155 | parent.prototype._onZoomEnd.call(this, e); 156 | } 157 | 158 | this._updateTooltip(this._currentLatLng); 159 | 160 | this._tooltip._container.style.visibility = 'inherit'; 161 | }, 162 | 163 | _getTooltipText: function() { 164 | var labelText = parent.prototype._getTooltipText.call(this); 165 | if (!this._drawing) { 166 | // put measurement string into main text 167 | labelText.text = labelText.subtext; 168 | delete labelText.subtext; 169 | } 170 | return labelText; 171 | } 172 | } 173 | } 174 | 175 | L.Nanomeasure.Distance = L.Draw.Polyline. 176 | extend(L.Nanomeasure.PolylineMixin(L.Draw.Polyline)). 177 | extend({ 178 | _finalize: function() { 179 | if(this._markers.length > 0) { 180 | this._currentLatLng = this._markers[this._markers.length - 1].getLatLng(); 181 | } 182 | }, 183 | 184 | _updateRunningMeasure: function(latlng, added) { 185 | var markersLength = this._markers.length, 186 | previousMarkerIndex, distance; 187 | 188 | if (this._markers.length === 1) { 189 | this._measurementRunningTotal = 0; 190 | } else { 191 | var zoom = this.options.ratioAtZoom !== undefined ? 192 | this.options.ratioAtZoom : this._map.getMaxZoom(); 193 | 194 | previousMarkerIndex = markersLength - (added ? 2 : 1); 195 | distance = this._map.project(latlng, zoom). 196 | distanceTo(this._map.project( 197 | this._markers[previousMarkerIndex].getLatLng(), zoom)); 198 | 199 | this._measurementRunningTotal += distance * 200 | this.options.nanometersPerPixel * (added ? 1 : -1); 201 | } 202 | }, 203 | 204 | _getMeasurementString: function() { 205 | var currentLatLng = this._currentLatLng, 206 | previousLatLng = this._markers[this._markers.length - 1].getLatLng(), 207 | distance; 208 | 209 | distance = this._measurementRunningTotal; 210 | if(this._drawing) { 211 | // calculate the distance from the last fixed point to the mouse position 212 | var zoom = this.options.ratioAtZoom !== undefined ? 213 | this.options.ratioAtZoom : this._map.getMaxZoom(); 214 | distance += this._map.project(currentLatLng, zoom).distanceTo( 215 | this._map.project(previousLatLng, zoom)) * this.options.nanometersPerPixel; 216 | } 217 | 218 | return L.Nanomeasure.Util.readableDistance(distance); 219 | }, 220 | }); 221 | 222 | L.Nanomeasure.Area = L.Draw.Polygon. 223 | extend(L.Nanomeasure.PolylineMixin(L.Draw.Polygon)). 224 | extend({ 225 | _finalize: function() { 226 | if(this._markers.length > 0) { 227 | this._currentLatLng = this._markers[0].getLatLng(); 228 | } 229 | 230 | if(this._shapeIsValid()) 231 | this._poly.addLatLng(this._markers[0].getLatLng()) 232 | }, 233 | 234 | _vertexChanged: function(latlng, added) { 235 | // Check to see if we should show the area 236 | if (!this.options.allowIntersection && this.options.showArea) { 237 | var zoom = this.options.ratioAtZoom !== undefined ? 238 | this.options.ratioAtZoom : this._map.getMaxZoom(), 239 | map = this._map; 240 | this._area = L.Nanomeasure.Util.polygonArea( 241 | this._poly.getLatLngs().map(function(latlng) { 242 | return map.project(latlng, zoom); 243 | })) * Math.pow(this.options.nanometersPerPixel, 2); 244 | } 245 | 246 | L.Draw.Polyline.prototype._vertexChanged.call(this, latlng, added); 247 | }, 248 | 249 | _getMeasurementString: function() { 250 | var area = this._area; 251 | 252 | if (!area) { 253 | return null; 254 | } 255 | 256 | return L.Nanomeasure.Util.readableArea(area); 257 | }, 258 | 259 | }); 260 | 261 | L.Control.Nanomeasure = L.Control.extend({ 262 | statics: { 263 | COORDINATES: 'Measure coordinates', 264 | DISTANCE: 'Measure distance', 265 | AREA: 'Measure area', 266 | }, 267 | 268 | options: { 269 | position: 'topleft', 270 | measureCoordinates: {}, 271 | measureDistance: {}, 272 | measureArea: {}, 273 | nanometersPerPixel: 1000, 274 | /*ratioAtZoom: undefined,*/ 275 | }, 276 | 277 | measurementTools: [], 278 | 279 | toggleArea: function() { 280 | if (this.measureArea.enabled()) { 281 | this.measureArea.disable(); 282 | } else { 283 | if(this.measureDistance.enabled()) { 284 | this.measureDistance.disable(); 285 | } 286 | 287 | this.measureArea.enable(); 288 | } 289 | }, 290 | 291 | addButton: function(className, title, measurementTool) { 292 | var baseClassName = 'leaflet-control-nanomeasure leaflet-control-nanomeasure'; 293 | var button = L.DomUtil.create('a', baseClassName + '-' + className, this._container); 294 | button.href = '#'; 295 | button.title = title; 296 | 297 | this.measurementTools.push(measurementTool); 298 | 299 | var $this = this; 300 | function toggleTool() { 301 | if(measurementTool.enabled()) { 302 | measurementTool.disable(); 303 | } else { 304 | $this.measurementTools.forEach(function(otherTool) { 305 | otherTool.disable(); 306 | }); 307 | measurementTool.enable(); 308 | } 309 | } 310 | 311 | L.DomEvent 312 | .addListener(button, 'click', L.DomEvent.stopPropagation) 313 | .addListener(button, 'click', L.DomEvent.preventDefault) 314 | .addListener(button, 'click', toggleTool, this); 315 | 316 | measurementTool.on('enabled', function() { 317 | L.DomUtil.addClass(button, 'enabled'); 318 | }, this); 319 | 320 | measurementTool.on('disabled', function() { 321 | L.DomUtil.removeClass(button, 'enabled'); 322 | }, this); 323 | }, 324 | 325 | onAdd: function(map) { 326 | this._container = L.DomUtil.create('div', 'leaflet-bar'); 327 | 328 | this.addButton('coordinates', L.Control.Nanomeasure.COORDINATES, 329 | new L.Nanomeasure.Coordinates(map, 330 | L.extend(this.options.measureCoordinates, { 331 | nanometersPerPixel: this.options.nanometersPerPixel, 332 | ratioAtZoom: this.options.ratioAtZoom, 333 | repeatMode: true, 334 | }))); 335 | 336 | this.addButton('distance', L.Control.Nanomeasure.DISTANCE, 337 | new L.Nanomeasure.Distance(map, 338 | L.extend(this.options.measureDistance, { 339 | nanometersPerPixel: this.options.nanometersPerPixel, 340 | ratioAtZoom: this.options.ratioAtZoom, 341 | }))); 342 | 343 | this.addButton('area', L.Control.Nanomeasure.AREA, 344 | new L.Nanomeasure.Area(map, 345 | L.extend(this.options.measureArea, { 346 | nanometersPerPixel: this.options.nanometersPerPixel, 347 | ratioAtZoom: this.options.ratioAtZoom, 348 | allowIntersection: false, 349 | showArea: true, 350 | }))); 351 | 352 | return this._container; 353 | } 354 | }); 355 | 356 | L.control.nanomeasure = function(options) { 357 | return new L.Control.Nanomeasure(options); 358 | }; 359 | -------------------------------------------------------------------------------- /public_html/vendor/leaflet.nanomeasure/images/measure-area-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitequark/groupXIV/d2140be3acbdd20286f9eddc5ec10b8db67b817a/public_html/vendor/leaflet.nanomeasure/images/measure-area-2x.png -------------------------------------------------------------------------------- /public_html/vendor/leaflet.nanomeasure/images/measure-coordinates-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitequark/groupXIV/d2140be3acbdd20286f9eddc5ec10b8db67b817a/public_html/vendor/leaflet.nanomeasure/images/measure-coordinates-2x.png -------------------------------------------------------------------------------- /public_html/vendor/leaflet.nanomeasure/images/measure-distance-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitequark/groupXIV/d2140be3acbdd20286f9eddc5ec10b8db67b817a/public_html/vendor/leaflet.nanomeasure/images/measure-distance-2x.png -------------------------------------------------------------------------------- /public_html/vendor/leaflet.nanoscale/Control.Nanoscale.js: -------------------------------------------------------------------------------- 1 | L.Control.Nanoscale = L.Control.extend({ 2 | options: { 3 | position: 'topright', 4 | maxWidth: 300, 5 | updateWhenIdle: false, 6 | nanometersPerPixel: 1000, 7 | /*ratioAtZoom: undefined,*/ 8 | }, 9 | 10 | onAdd: function (map) { 11 | this._map = map; 12 | 13 | var className = 'leaflet-control-scale', 14 | container = L.DomUtil.create('div', className), 15 | options = this.options; 16 | 17 | this._addScales(options, className, container); 18 | 19 | map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this); 20 | map.whenReady(this._update, this); 21 | 22 | return container; 23 | }, 24 | 25 | onRemove: function (map) { 26 | map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this); 27 | }, 28 | 29 | _addScales: function (options, className, container) { 30 | this._scale = L.DomUtil.create('div', className + '-line', container); 31 | }, 32 | 33 | _update: function () { 34 | var options = this.options, 35 | 36 | bounds = this._map.getBounds(), 37 | maxZoom = options.ratioAtZoom !== undefined ? options.ratioAtZoom : 38 | this._map.getMaxZoom(), 39 | dist = (this._map.project(bounds.getNorthEast(), maxZoom).x - 40 | this._map.project(bounds.getSouthWest(), maxZoom).x), 41 | 42 | size = this._map.getSize(), 43 | maxNanometers = 0; 44 | 45 | if (size.x > 0) { 46 | maxNanometers = dist * (options.maxWidth / size.x) * options.nanometersPerPixel; 47 | } 48 | 49 | this._updateScales(options, maxNanometers); 50 | }, 51 | 52 | _updateScales: function (options, maxNanometers) { 53 | var nanometers = this._getRoundNum(maxNanometers); 54 | 55 | this._scale.style.width = this._getScaleWidth(nanometers / maxNanometers) + 'px'; 56 | this._scale.innerHTML = 57 | nanometers < 1000 ? nanometers + ' nm' : 58 | nanometers < 1000000 ? (nanometers / 1000) + ' \u00b5m' : 59 | (nanometers / 1000000) + ' mm'; 60 | }, 61 | 62 | _getScaleWidth: function (ratio) { 63 | return Math.round(this.options.maxWidth * ratio) - 10; 64 | }, 65 | 66 | _getRoundNum: function (num) { 67 | var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1), 68 | d = num / pow10; 69 | 70 | d = d >= 10 ? 10 : d >= 5 ? 5 : d >= 3 ? 3 : d >= 2 ? 2 : 1; 71 | 72 | return pow10 * d; 73 | } 74 | }); 75 | 76 | L.control.nanoscale = function (options) { 77 | return new L.Control.Nanoscale(options); 78 | }; 79 | -------------------------------------------------------------------------------- /public_html/vendor/leaflet.tilelayer.fallback/leaflet.tilelayer.fallback.js: -------------------------------------------------------------------------------- 1 | /*! 2 | Leaflet.TileLayer.Fallback 1.0.4+e36cde9 3 | (c) 2015-2018 Boris Seang 4 | License Apache-2.0 5 | */ 6 | !function(i,e){"function"==typeof define&&define.amd?define(["leaflet"],e):e("object"==typeof module&&module.exports?require("leaflet"):i.L)}(this,function(i){i.TileLayer.Fallback=i.TileLayer.extend({options:{minNativeZoom:0},initialize:function(e,r){i.TileLayer.prototype.initialize.call(this,e,r)},createTile:function(e,r){var t=i.TileLayer.prototype.createTile.call(this,e,r);return t._originalCoords=e,t._originalSrc=t.src,t},_createCurrentCoords:function(i){var e=this._wrapCoords(i);return e.fallback=!0,e},_originalTileOnError:i.TileLayer.prototype._tileOnError,_tileOnError:function(i,e,r){var t,l,o,a=this,n=e._originalCoords,c=e._currentCoords=e._currentCoords||a._createCurrentCoords(n),s=e._fallbackZoom=void 0===e._fallbackZoom?n.z-1:e._fallbackZoom-1,f=e._fallbackScale=2*(e._fallbackScale||1),p=a.getTileSize(),u=e.style;if(s svg, 9 | .leaflet-pane > canvas, 10 | .leaflet-zoom-box, 11 | .leaflet-image-layer, 12 | .leaflet-layer { 13 | position: absolute; 14 | left: 0; 15 | top: 0; 16 | } 17 | .leaflet-container { 18 | overflow: hidden; 19 | } 20 | .leaflet-tile, 21 | .leaflet-marker-icon, 22 | .leaflet-marker-shadow { 23 | -webkit-user-select: none; 24 | -moz-user-select: none; 25 | user-select: none; 26 | -webkit-user-drag: none; 27 | } 28 | /* Safari renders non-retina tile on retina better with this, but Chrome is worse */ 29 | .leaflet-safari .leaflet-tile { 30 | image-rendering: -webkit-optimize-contrast; 31 | } 32 | /* hack that prevents hw layers "stretching" when loading new tiles */ 33 | .leaflet-safari .leaflet-tile-container { 34 | width: 1600px; 35 | height: 1600px; 36 | -webkit-transform-origin: 0 0; 37 | } 38 | .leaflet-marker-icon, 39 | .leaflet-marker-shadow { 40 | display: block; 41 | } 42 | /* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */ 43 | /* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */ 44 | .leaflet-container .leaflet-overlay-pane svg, 45 | .leaflet-container .leaflet-marker-pane img, 46 | .leaflet-container .leaflet-shadow-pane img, 47 | .leaflet-container .leaflet-tile-pane img, 48 | .leaflet-container img.leaflet-image-layer, 49 | .leaflet-container .leaflet-tile { 50 | max-width: none !important; 51 | max-height: none !important; 52 | } 53 | 54 | .leaflet-container.leaflet-touch-zoom { 55 | -ms-touch-action: pan-x pan-y; 56 | touch-action: pan-x pan-y; 57 | } 58 | .leaflet-container.leaflet-touch-drag { 59 | -ms-touch-action: pinch-zoom; 60 | /* Fallback for FF which doesn't support pinch-zoom */ 61 | touch-action: none; 62 | touch-action: pinch-zoom; 63 | } 64 | .leaflet-container.leaflet-touch-drag.leaflet-touch-zoom { 65 | -ms-touch-action: none; 66 | touch-action: none; 67 | } 68 | .leaflet-container { 69 | -webkit-tap-highlight-color: transparent; 70 | } 71 | .leaflet-container a { 72 | -webkit-tap-highlight-color: rgba(51, 181, 229, 0.4); 73 | } 74 | .leaflet-tile { 75 | filter: inherit; 76 | visibility: hidden; 77 | } 78 | .leaflet-tile-loaded { 79 | visibility: inherit; 80 | } 81 | .leaflet-zoom-box { 82 | width: 0; 83 | height: 0; 84 | -moz-box-sizing: border-box; 85 | box-sizing: border-box; 86 | z-index: 800; 87 | } 88 | /* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */ 89 | .leaflet-overlay-pane svg { 90 | -moz-user-select: none; 91 | } 92 | 93 | .leaflet-pane { z-index: 400; } 94 | 95 | .leaflet-tile-pane { z-index: 200; } 96 | .leaflet-overlay-pane { z-index: 400; } 97 | .leaflet-shadow-pane { z-index: 500; } 98 | .leaflet-marker-pane { z-index: 600; } 99 | .leaflet-tooltip-pane { z-index: 650; } 100 | .leaflet-popup-pane { z-index: 700; } 101 | 102 | .leaflet-map-pane canvas { z-index: 100; } 103 | .leaflet-map-pane svg { z-index: 200; } 104 | 105 | .leaflet-vml-shape { 106 | width: 1px; 107 | height: 1px; 108 | } 109 | .lvml { 110 | behavior: url(#default#VML); 111 | display: inline-block; 112 | position: absolute; 113 | } 114 | 115 | 116 | /* control positioning */ 117 | 118 | .leaflet-control { 119 | position: relative; 120 | z-index: 800; 121 | pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */ 122 | pointer-events: auto; 123 | } 124 | .leaflet-top, 125 | .leaflet-bottom { 126 | position: absolute; 127 | z-index: 1000; 128 | pointer-events: none; 129 | } 130 | .leaflet-top { 131 | top: 0; 132 | } 133 | .leaflet-right { 134 | right: 0; 135 | } 136 | .leaflet-bottom { 137 | bottom: 0; 138 | } 139 | .leaflet-left { 140 | left: 0; 141 | } 142 | .leaflet-control { 143 | float: left; 144 | clear: both; 145 | } 146 | .leaflet-right .leaflet-control { 147 | float: right; 148 | } 149 | .leaflet-top .leaflet-control { 150 | margin-top: 10px; 151 | } 152 | .leaflet-bottom .leaflet-control { 153 | margin-bottom: 10px; 154 | } 155 | .leaflet-left .leaflet-control { 156 | margin-left: 10px; 157 | } 158 | .leaflet-right .leaflet-control { 159 | margin-right: 10px; 160 | } 161 | 162 | 163 | /* zoom and fade animations */ 164 | 165 | .leaflet-fade-anim .leaflet-tile { 166 | will-change: opacity; 167 | } 168 | .leaflet-fade-anim .leaflet-popup { 169 | opacity: 0; 170 | -webkit-transition: opacity 0.2s linear; 171 | -moz-transition: opacity 0.2s linear; 172 | transition: opacity 0.2s linear; 173 | } 174 | .leaflet-fade-anim .leaflet-map-pane .leaflet-popup { 175 | opacity: 1; 176 | } 177 | .leaflet-zoom-animated { 178 | -webkit-transform-origin: 0 0; 179 | -ms-transform-origin: 0 0; 180 | transform-origin: 0 0; 181 | } 182 | .leaflet-zoom-anim .leaflet-zoom-animated { 183 | will-change: transform; 184 | } 185 | .leaflet-zoom-anim .leaflet-zoom-animated { 186 | -webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1); 187 | -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1); 188 | transition: transform 0.25s cubic-bezier(0,0,0.25,1); 189 | } 190 | .leaflet-zoom-anim .leaflet-tile, 191 | .leaflet-pan-anim .leaflet-tile { 192 | -webkit-transition: none; 193 | -moz-transition: none; 194 | transition: none; 195 | } 196 | 197 | .leaflet-zoom-anim .leaflet-zoom-hide { 198 | visibility: hidden; 199 | } 200 | 201 | 202 | /* cursors */ 203 | 204 | .leaflet-interactive { 205 | cursor: pointer; 206 | } 207 | .leaflet-grab { 208 | cursor: -webkit-grab; 209 | cursor: -moz-grab; 210 | cursor: grab; 211 | } 212 | .leaflet-crosshair, 213 | .leaflet-crosshair .leaflet-interactive { 214 | cursor: crosshair; 215 | } 216 | .leaflet-popup-pane, 217 | .leaflet-control { 218 | cursor: auto; 219 | } 220 | .leaflet-dragging .leaflet-grab, 221 | .leaflet-dragging .leaflet-grab .leaflet-interactive, 222 | .leaflet-dragging .leaflet-marker-draggable { 223 | cursor: move; 224 | cursor: -webkit-grabbing; 225 | cursor: -moz-grabbing; 226 | cursor: grabbing; 227 | } 228 | 229 | /* marker & overlays interactivity */ 230 | .leaflet-marker-icon, 231 | .leaflet-marker-shadow, 232 | .leaflet-image-layer, 233 | .leaflet-pane > svg path, 234 | .leaflet-tile-container { 235 | pointer-events: none; 236 | } 237 | 238 | .leaflet-marker-icon.leaflet-interactive, 239 | .leaflet-image-layer.leaflet-interactive, 240 | .leaflet-pane > svg path.leaflet-interactive { 241 | pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */ 242 | pointer-events: auto; 243 | } 244 | 245 | /* visual tweaks */ 246 | 247 | .leaflet-container { 248 | background: #ddd; 249 | outline: 0; 250 | } 251 | .leaflet-container a { 252 | color: #0078A8; 253 | } 254 | .leaflet-container a.leaflet-active { 255 | outline: 2px solid orange; 256 | } 257 | .leaflet-zoom-box { 258 | border: 2px dotted #38f; 259 | background: rgba(255,255,255,0.5); 260 | } 261 | 262 | 263 | /* general typography */ 264 | .leaflet-container { 265 | font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif; 266 | } 267 | 268 | 269 | /* general toolbar styles */ 270 | 271 | .leaflet-bar { 272 | box-shadow: 0 1px 5px rgba(0,0,0,0.65); 273 | border-radius: 4px; 274 | } 275 | .leaflet-bar a, 276 | .leaflet-bar a:hover { 277 | background-color: #fff; 278 | border-bottom: 1px solid #ccc; 279 | width: 26px; 280 | height: 26px; 281 | line-height: 26px; 282 | display: block; 283 | text-align: center; 284 | text-decoration: none; 285 | color: black; 286 | } 287 | .leaflet-bar a, 288 | .leaflet-control-layers-toggle { 289 | background-position: 50% 50%; 290 | background-repeat: no-repeat; 291 | display: block; 292 | } 293 | .leaflet-bar a:hover { 294 | background-color: #f4f4f4; 295 | } 296 | .leaflet-bar a:first-child { 297 | border-top-left-radius: 4px; 298 | border-top-right-radius: 4px; 299 | } 300 | .leaflet-bar a:last-child { 301 | border-bottom-left-radius: 4px; 302 | border-bottom-right-radius: 4px; 303 | border-bottom: none; 304 | } 305 | .leaflet-bar a.leaflet-disabled { 306 | cursor: default; 307 | background-color: #f4f4f4; 308 | color: #bbb; 309 | } 310 | 311 | .leaflet-touch .leaflet-bar a { 312 | width: 30px; 313 | height: 30px; 314 | line-height: 30px; 315 | } 316 | .leaflet-touch .leaflet-bar a:first-child { 317 | border-top-left-radius: 2px; 318 | border-top-right-radius: 2px; 319 | } 320 | .leaflet-touch .leaflet-bar a:last-child { 321 | border-bottom-left-radius: 2px; 322 | border-bottom-right-radius: 2px; 323 | } 324 | 325 | /* zoom control */ 326 | 327 | .leaflet-control-zoom-in, 328 | .leaflet-control-zoom-out { 329 | font: bold 18px 'Lucida Console', Monaco, monospace; 330 | text-indent: 1px; 331 | } 332 | 333 | .leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out { 334 | font-size: 22px; 335 | } 336 | 337 | 338 | /* layers control */ 339 | 340 | .leaflet-control-layers { 341 | box-shadow: 0 1px 5px rgba(0,0,0,0.4); 342 | background: #fff; 343 | border-radius: 5px; 344 | } 345 | .leaflet-control-layers-toggle { 346 | background-image: url(images/layers.png); 347 | width: 36px; 348 | height: 36px; 349 | } 350 | .leaflet-retina .leaflet-control-layers-toggle { 351 | background-image: url(images/layers-2x.png); 352 | background-size: 26px 26px; 353 | } 354 | .leaflet-touch .leaflet-control-layers-toggle { 355 | width: 44px; 356 | height: 44px; 357 | } 358 | .leaflet-control-layers .leaflet-control-layers-list, 359 | .leaflet-control-layers-expanded .leaflet-control-layers-toggle { 360 | display: none; 361 | } 362 | .leaflet-control-layers-expanded .leaflet-control-layers-list { 363 | display: block; 364 | position: relative; 365 | } 366 | .leaflet-control-layers-expanded { 367 | padding: 6px 10px 6px 6px; 368 | color: #333; 369 | background: #fff; 370 | } 371 | .leaflet-control-layers-scrollbar { 372 | overflow-y: scroll; 373 | overflow-x: hidden; 374 | padding-right: 5px; 375 | } 376 | .leaflet-control-layers-selector { 377 | margin-top: 2px; 378 | position: relative; 379 | top: 1px; 380 | } 381 | .leaflet-control-layers label { 382 | display: block; 383 | } 384 | .leaflet-control-layers-separator { 385 | height: 0; 386 | border-top: 1px solid #ddd; 387 | margin: 5px -10px 5px -6px; 388 | } 389 | 390 | /* Default icon URLs */ 391 | .leaflet-default-icon-path { 392 | background-image: url(images/marker-icon.png); 393 | } 394 | 395 | 396 | /* attribution and scale controls */ 397 | 398 | .leaflet-container .leaflet-control-attribution { 399 | background: #fff; 400 | background: rgba(255, 255, 255, 0.7); 401 | margin: 0; 402 | } 403 | .leaflet-control-attribution, 404 | .leaflet-control-scale-line { 405 | padding: 0 5px; 406 | color: #333; 407 | } 408 | .leaflet-control-attribution a { 409 | text-decoration: none; 410 | } 411 | .leaflet-control-attribution a:hover { 412 | text-decoration: underline; 413 | } 414 | .leaflet-container .leaflet-control-attribution, 415 | .leaflet-container .leaflet-control-scale { 416 | font-size: 11px; 417 | } 418 | .leaflet-left .leaflet-control-scale { 419 | margin-left: 5px; 420 | } 421 | .leaflet-bottom .leaflet-control-scale { 422 | margin-bottom: 5px; 423 | } 424 | .leaflet-control-scale-line { 425 | border: 2px solid #777; 426 | border-top: none; 427 | line-height: 1.1; 428 | padding: 2px 5px 1px; 429 | font-size: 11px; 430 | white-space: nowrap; 431 | overflow: hidden; 432 | -moz-box-sizing: border-box; 433 | box-sizing: border-box; 434 | 435 | background: #fff; 436 | background: rgba(255, 255, 255, 0.5); 437 | } 438 | .leaflet-control-scale-line:not(:first-child) { 439 | border-top: 2px solid #777; 440 | border-bottom: none; 441 | margin-top: -2px; 442 | } 443 | .leaflet-control-scale-line:not(:first-child):not(:last-child) { 444 | border-bottom: 2px solid #777; 445 | } 446 | 447 | .leaflet-touch .leaflet-control-attribution, 448 | .leaflet-touch .leaflet-control-layers, 449 | .leaflet-touch .leaflet-bar { 450 | box-shadow: none; 451 | } 452 | .leaflet-touch .leaflet-control-layers, 453 | .leaflet-touch .leaflet-bar { 454 | border: 2px solid rgba(0,0,0,0.2); 455 | background-clip: padding-box; 456 | } 457 | 458 | 459 | /* popup */ 460 | 461 | .leaflet-popup { 462 | position: absolute; 463 | text-align: center; 464 | margin-bottom: 20px; 465 | } 466 | .leaflet-popup-content-wrapper { 467 | padding: 1px; 468 | text-align: left; 469 | border-radius: 12px; 470 | } 471 | .leaflet-popup-content { 472 | margin: 13px 19px; 473 | line-height: 1.4; 474 | } 475 | .leaflet-popup-content p { 476 | margin: 18px 0; 477 | } 478 | .leaflet-popup-tip-container { 479 | width: 40px; 480 | height: 20px; 481 | position: absolute; 482 | left: 50%; 483 | margin-left: -20px; 484 | overflow: hidden; 485 | pointer-events: none; 486 | } 487 | .leaflet-popup-tip { 488 | width: 17px; 489 | height: 17px; 490 | padding: 1px; 491 | 492 | margin: -10px auto 0; 493 | 494 | -webkit-transform: rotate(45deg); 495 | -moz-transform: rotate(45deg); 496 | -ms-transform: rotate(45deg); 497 | transform: rotate(45deg); 498 | } 499 | .leaflet-popup-content-wrapper, 500 | .leaflet-popup-tip { 501 | background: white; 502 | color: #333; 503 | box-shadow: 0 3px 14px rgba(0,0,0,0.4); 504 | } 505 | .leaflet-container a.leaflet-popup-close-button { 506 | position: absolute; 507 | top: 0; 508 | right: 0; 509 | padding: 4px 4px 0 0; 510 | border: none; 511 | text-align: center; 512 | width: 18px; 513 | height: 14px; 514 | font: 16px/14px Tahoma, Verdana, sans-serif; 515 | color: #c3c3c3; 516 | text-decoration: none; 517 | font-weight: bold; 518 | background: transparent; 519 | } 520 | .leaflet-container a.leaflet-popup-close-button:hover { 521 | color: #999; 522 | } 523 | .leaflet-popup-scrolled { 524 | overflow: auto; 525 | border-bottom: 1px solid #ddd; 526 | border-top: 1px solid #ddd; 527 | } 528 | 529 | .leaflet-oldie .leaflet-popup-content-wrapper { 530 | zoom: 1; 531 | } 532 | .leaflet-oldie .leaflet-popup-tip { 533 | width: 24px; 534 | margin: 0 auto; 535 | 536 | -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)"; 537 | filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678); 538 | } 539 | .leaflet-oldie .leaflet-popup-tip-container { 540 | margin-top: -1px; 541 | } 542 | 543 | .leaflet-oldie .leaflet-control-zoom, 544 | .leaflet-oldie .leaflet-control-layers, 545 | .leaflet-oldie .leaflet-popup-content-wrapper, 546 | .leaflet-oldie .leaflet-popup-tip { 547 | border: 1px solid #999; 548 | } 549 | 550 | 551 | /* div icon */ 552 | 553 | .leaflet-div-icon { 554 | background: #fff; 555 | border: 1px solid #666; 556 | } 557 | 558 | 559 | /* Tooltip */ 560 | /* Base styles for the element that has a tooltip */ 561 | .leaflet-tooltip { 562 | position: absolute; 563 | padding: 6px; 564 | background-color: #fff; 565 | border: 1px solid #fff; 566 | border-radius: 3px; 567 | color: #222; 568 | white-space: nowrap; 569 | -webkit-user-select: none; 570 | -moz-user-select: none; 571 | -ms-user-select: none; 572 | user-select: none; 573 | pointer-events: none; 574 | box-shadow: 0 1px 3px rgba(0,0,0,0.4); 575 | } 576 | .leaflet-tooltip.leaflet-clickable { 577 | cursor: pointer; 578 | pointer-events: auto; 579 | } 580 | .leaflet-tooltip-top:before, 581 | .leaflet-tooltip-bottom:before, 582 | .leaflet-tooltip-left:before, 583 | .leaflet-tooltip-right:before { 584 | position: absolute; 585 | pointer-events: none; 586 | border: 6px solid transparent; 587 | background: transparent; 588 | content: ""; 589 | } 590 | 591 | /* Directions */ 592 | 593 | .leaflet-tooltip-bottom { 594 | margin-top: 6px; 595 | } 596 | .leaflet-tooltip-top { 597 | margin-top: -6px; 598 | } 599 | .leaflet-tooltip-bottom:before, 600 | .leaflet-tooltip-top:before { 601 | left: 50%; 602 | margin-left: -6px; 603 | } 604 | .leaflet-tooltip-top:before { 605 | bottom: 0; 606 | margin-bottom: -12px; 607 | border-top-color: #fff; 608 | } 609 | .leaflet-tooltip-bottom:before { 610 | top: 0; 611 | margin-top: -12px; 612 | margin-left: -6px; 613 | border-bottom-color: #fff; 614 | } 615 | .leaflet-tooltip-left { 616 | margin-left: -6px; 617 | } 618 | .leaflet-tooltip-right { 619 | margin-left: 6px; 620 | } 621 | .leaflet-tooltip-left:before, 622 | .leaflet-tooltip-right:before { 623 | top: 50%; 624 | margin-top: -6px; 625 | } 626 | .leaflet-tooltip-left:before { 627 | right: 0; 628 | margin-right: -12px; 629 | border-left-color: #fff; 630 | } 631 | .leaflet-tooltip-right:before { 632 | left: 0; 633 | margin-left: -12px; 634 | border-right-color: #fff; 635 | } 636 | -------------------------------------------------------------------------------- /public_html/viewer.js: -------------------------------------------------------------------------------- 1 | function parseHash() { 2 | var output = {}; 3 | var pairs = window.location.hash.substring(1).split("&"); 4 | pairs.forEach(function(pair) { 5 | var parts = pair.split("=", 2), 6 | key = parts[0], 7 | value = parts[1]; 8 | output[key] = value; 9 | }); 10 | return output; 11 | } 12 | 13 | function initViewer(options, url) { 14 | document.title = options.name + " \u00b7 GroupXIV microphotography viewer" 15 | 16 | var map = GroupXIV({ 17 | viewport: "viewer", 18 | scale: options.scale, 19 | tileSize: options.tileSize, 20 | layers: options.layers, 21 | tilesAlignedTopLeft: options.tilesAlignedTopLeft, 22 | }); 23 | 24 | function moveMap(params) { 25 | if(params.x && params.y && params.z) { 26 | var center = map.unproject([parseInt(params.x), parseInt(params.y)], map.getMaxZoom() - 1), 27 | zoom = parseInt(params.z); 28 | map.setView(center, zoom); 29 | } 30 | } 31 | 32 | map.on('moveend', function(e) { 33 | var center = map.project(map.getCenter(), map.getMaxZoom() - 1), 34 | zoom = map.getZoom(); 35 | var state = "#"; 36 | if(options["canChangeURL"]) { 37 | state += "url=" + url + "&"; 38 | } 39 | state += "x=" + (center.x|0) + "&"; 40 | state += "y=" + (center.y|0) + "&"; 41 | state += "z=" + zoom; 42 | history.replaceState(null, null, state); 43 | }); 44 | 45 | moveMap(parseHash()); 46 | window.onhashchange = function() { 47 | var params = parseHash(); 48 | if(options["canChangeURL"] && params["url"] != url) { 49 | window.location.reload(); 50 | } else { 51 | moveMap(params); 52 | } 53 | } 54 | } 55 | 56 | function loadViewer(url, options) { 57 | var viewerOptions = (options === undefined) ? {} : options; 58 | var canChangeURL = (url === undefined); 59 | if (canChangeURL) { 60 | viewerOptions["canChangeURL"] = canChangeURL; 61 | var params = parseHash(); 62 | url = params["url"]; 63 | } 64 | 65 | var req = new XMLHttpRequest(); 66 | req.onload = function() { 67 | var responseOptions = JSON.parse(req.responseText); 68 | responseOptions.layers.forEach(function(layer) { 69 | layer.URL = url + "/../" + layer.URL; 70 | layer.URL = layer.URL.replace(/[^\/]+\/..(\/|$)/, ''); 71 | }); 72 | initViewer(Object.assign({}, viewerOptions, responseOptions), url); 73 | }; 74 | req.onerror = function() { 75 | var error = document.createElement("div"); 76 | error.className = "error"; 77 | error.innerText = "Cannot load " + url; 78 | document.getElementById("viewer").appendChild(error); 79 | window.onhashchange = function() { 80 | var params = parseHash(); 81 | if (canChangeURL && params["url"] != url) { 82 | window.location.reload(); 83 | } 84 | } 85 | } 86 | req.open("get", url, true); 87 | req.send(); 88 | } 89 | -------------------------------------------------------------------------------- /tile_cutter/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitequark/groupXIV/d2140be3acbdd20286f9eddc5ec10b8db67b817a/tile_cutter/__init__.py -------------------------------------------------------------------------------- /tile_cutter/__main__.py: -------------------------------------------------------------------------------- 1 | import math, time, os, argparse, logging, json 2 | from wand.image import Image 3 | 4 | parser = argparse.ArgumentParser( 5 | prog='tile_cutter', 6 | description='Cuts large images into tiles.') 7 | parser.add_argument('--tile-size', metavar='SIZE', type=int, default=512, 8 | help='Tile size (width and height)') 9 | parser.add_argument('-v', '--verbose', action='store_true', 10 | help='Log debugging information') 11 | parser.add_argument('image', type=argparse.FileType('rb'), 12 | help='Source image') 13 | args = parser.parse_args() 14 | 15 | if args.verbose: 16 | logging.basicConfig(level=logging.DEBUG) 17 | else: 18 | logging.basicConfig(level=logging.INFO) 19 | 20 | layers = [] 21 | 22 | tile_size = args.tile_size 23 | logging.info("tile size: %dx%d", tile_size, tile_size) 24 | 25 | with Image(file=args.image) as source: 26 | logging.info("image size: %dx%d", source.width, source.height) 27 | 28 | # every zoom level has 2x more tiles 29 | max_zoom = math.ceil(math.log(max(source.size) / args.tile_size, 2)) 30 | logging.info("zoom levels: 1-%d", max_zoom) 31 | 32 | image_size = args.tile_size * (2 ** max_zoom) 33 | offset_x, offset_y = tuple((image_size - orig) // 2 for orig in source.size) 34 | logging.info("tiled size: %dx%d-%d-%d", image_size, image_size, offset_x, offset_y) 35 | 36 | layers.append({ 37 | "name": "???", 38 | "URL": os.path.basename(args.image.name), 39 | "width": source.width, 40 | "height": source.height, 41 | "tileSize": args.tile_size, 42 | "imageSize": image_size 43 | }) 44 | 45 | square_source = Image(width=image_size, height=image_size) 46 | square_source.composite(source, 47 | (square_source.width - source.width) // 2, 48 | (square_source.height - source.height) // 2) 49 | 50 | for z in range(1, max_zoom + 1): 51 | source_size = int(args.tile_size * (2 ** (max_zoom - z))) 52 | logging.info("zoom level %d: source %dx%d", z, source_size, source_size) 53 | 54 | current_image = 0 55 | total_images = (image_size // source_size) ** 2 56 | start_time = last_report_time = time.perf_counter() 57 | 58 | for y in range(0, image_size // source_size): 59 | for x in range(0, image_size // source_size): 60 | crop_x, crop_y = x * source_size, y * source_size 61 | path = "%s-tiles/%d/%d/%d.png" % (args.image.name, z, x, y) 62 | logging.debug("tile %s: source %dx%d%+d%+d", 63 | path, source_size, source_size, crop_x, crop_y) 64 | 65 | with square_source.clone() as tile: 66 | tile.crop(crop_x, crop_y, width=source_size, height=source_size) 67 | tile.resize(tile_size, tile_size) 68 | os.makedirs(os.path.dirname(path), exist_ok=True) 69 | tile.save(filename=path) 70 | 71 | current_image += 1 72 | if time.perf_counter() - last_report_time > 1: 73 | last_report_time = time.perf_counter() 74 | eta = (last_report_time - start_time) / current_image * \ 75 | (total_images - current_image) 76 | logging.info("completion: %.2f%% (ETA: %dh%dm%ds)", 77 | current_image / total_images * 100, 78 | eta // 3600, (eta % 3600) // 60, eta % 60) 79 | 80 | with open("%s.json" % args.image.name, "w") as descr: 81 | descr.write(json.dumps({ 82 | "name": "???", 83 | "scale": None, 84 | "layers": layers 85 | })) 86 | logging.info("image description written to: %s" % descr.name) 87 | 88 | logging.info("done") 89 | --------------------------------------------------------------------------------