nakarte

Source code of https://map.sikmir.ru (fork)
git clone git://git.sikmir.ru/nakarte
Log | Files | Refs | LICENSE

decoration.grid.js (6172B)


      1 import {PrintStaticLayer} from './decorations';
      2 import L from 'leaflet';
      3 
      4 function radians(degrees) {
      5     return degrees * Math.PI / 180;
      6 }
      7 
      8 class Grid extends PrintStaticLayer {
      9     minGridIntervalMm = 15;
     10 
     11     fontSizeMm = 3;
     12     font = 'verdana';
     13     paddingMm = 1;
     14 
     15     /* eslint-disable array-element-newline*/
     16     intervals = [
     17         1, 1.5, 2, 3.3, 5, 7.5,
     18         10, 15, 20, 33, 50, 75,
     19         100, 150, 200, 333, 500, 750,
     20         1000, 1500, 2000, 4000, 5000, 7500,
     21         10000, 15000, 20000, 40000, 50000, 75000,
     22         100000, 150000, 200000, 400000, 500000, 750000,
     23         1000000, 1500000, 2000000, 4000000, 5000000, 7500000
     24     ];
     25     /* eslint-enable array-element-newline*/
     26 
     27     getGridInterval(printOptions) {
     28         const minGridIntervalM = this.minGridIntervalMm / 10 * printOptions.scale;
     29         let intervalM;
     30         for (intervalM of this.intervals) {
     31             if (intervalM > minGridIntervalM) {
     32                 break;
     33             }
     34         }
     35         return intervalM;
     36     }
     37 
     38     formatDistance(x) {
     39         let unit;
     40         if (x < 1000) {
     41             unit = 'm';
     42         } else {
     43             x /= 1000;
     44             unit = 'km';
     45         }
     46         if (x % 1) {
     47             x = x.toFixed(1);
     48         }
     49         return `${x} ${unit}`;
     50     }
     51 
     52     _drawGrid(canvas, printOptions) {
     53         const metersPerDegree = L.Projection.SphericalMercator.R * Math.PI / 180;
     54         const ctx = canvas.getContext('2d');
     55         ctx.beginPath();
     56         const pixelsPerMm = 1 / 25.4 * printOptions.resolution;
     57         const intervalM = this.getGridInterval(printOptions);
     58         const width = printOptions.destPixelSize.x;
     59         const height = printOptions.destPixelSize.y;
     60         const mercatorBounds = L.bounds(
     61             L.Projection.SphericalMercator.project(printOptions.latLngBounds.getNorthWest()),
     62             L.Projection.SphericalMercator.project(printOptions.latLngBounds.getSouthEast())
     63         );
     64         const canvasToMercatorScale = mercatorBounds.getSize().unscaleBy(printOptions.destPixelSize);
     65         const rows = [];
     66         let y = height;
     67         while (true) {
     68             let yMerc = mercatorBounds.max.y - y * canvasToMercatorScale.y;
     69             let lat = L.Projection.SphericalMercator.unproject(L.point(0, yMerc)).lat;
     70             rows.push({lat, y});
     71             if (y < 0) {
     72                 break;
     73             }
     74             let lat2 = lat + intervalM / metersPerDegree;
     75             let yMerc2 = L.Projection.SphericalMercator.project(L.latLng(lat2, 0)).y;
     76             y = (mercatorBounds.max.y - yMerc2) / canvasToMercatorScale.y;
     77         }
     78         const lineWidthMm = 0.15;
     79         let lineWidthPx = lineWidthMm * pixelsPerMm;
     80         for (let {color, offset} of [
     81             {color: '#D9D9D9', offset: lineWidthPx / 2},
     82             {color: '#8C8C8C', offset: -lineWidthPx / 2},
     83         ]) {
     84             ctx.beginPath();
     85 
     86             ctx.lineWidth = lineWidthPx;
     87             ctx.strokeStyle = color;
     88 
     89             for ({y} of rows) {
     90                 ctx.moveTo(0, y + offset);
     91                 ctx.lineTo(width, y + offset);
     92             }
     93             const pageCanvasCenterX = printOptions.destPixelSize.x / 2;
     94             for (let direction of [-1, 1]) {
     95                 let colN = 0;
     96                 while (true) {
     97                     let firstRow = true;
     98                     let hasPointInPage = false;
     99                     for (let {lat, y} of rows) {
    100                         let dx = colN * intervalM / Math.cos(radians(lat)) / canvasToMercatorScale.x;
    101                         // eslint-disable-next-line max-depth
    102                         if (dx < pageCanvasCenterX) {
    103                             hasPointInPage = true;
    104                         }
    105                         // eslint-disable-next-line max-depth
    106                         if (firstRow) {
    107                             ctx.moveTo(pageCanvasCenterX + dx * direction + offset, y);
    108                         } else {
    109                             ctx.lineTo(pageCanvasCenterX + dx * direction + offset, y);
    110                         }
    111                         firstRow = false;
    112                     }
    113                     if (!hasPointInPage) {
    114                         break;
    115                     }
    116                     colN += 1;
    117                 }
    118             }
    119             ctx.stroke();
    120         }
    121     }
    122 
    123     _drawLabel(canvas, printOptions) {
    124         const intervalM = this.getGridInterval(printOptions);
    125         const height = printOptions.destPixelSize.y;
    126         const ctx = canvas.getContext('2d');
    127         const caption = 'Grid ' + this.formatDistance(intervalM);
    128         const fontSize = this.fontSizeMm / 25.4 * printOptions.resolution;
    129         const padding = this.paddingMm / 25.4 * printOptions.resolution;
    130         ctx.font = `${fontSize}px ${this.font}`;
    131         const textWidth = ctx.measureText(caption).width;
    132         ctx.textBaseline = 'bottom';
    133         ctx.fillStyle = 'rgba(255, 255, 255, 0.7)';
    134         ctx.fillRect(0, height - fontSize - 2 * padding, textWidth + 2 * padding, fontSize + 2 * padding);
    135         ctx.fillStyle = '#000000';
    136         ctx.fillText(caption, padding, height - padding);
    137     }
    138 
    139     async getTilesInfo(printOptions) {
    140         return {
    141             iterateTilePromises: (function*() {
    142                 yield {
    143                     tilePromise: Promise.resolve({
    144                             draw: (canvas) => this._drawGrid(canvas, printOptions),
    145                             isOverlay: true,
    146                             overlaySolid: false
    147                         }
    148                     ),
    149                     abortLoading: () => {
    150                         // no actions needed
    151                     }
    152                 };
    153                 yield {
    154                     tilePromise: Promise.resolve({
    155                             draw: (canvas) => this._drawLabel(canvas, printOptions),
    156                             isOverlay: true,
    157                             overlaySolid: true
    158                         }
    159                     ),
    160                     abortLoading: () => {
    161                         // no actions needed
    162                     }
    163                 };
    164             }).bind(this),
    165             count: 2
    166         };
    167     }
    168 }
    169 
    170 export {Grid};