diff --git a/example/index.module.html b/example/index.module.html index f8e7285..8fb2a21 100644 --- a/example/index.module.html +++ b/example/index.module.html @@ -2,101 +2,66 @@ Simple Map - - + + - + -
-
- - - - +
+
+ + + +
- diff --git a/package-lock.json b/package-lock.json index da29e59..ed47ec9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1884,6 +1884,11 @@ "array-find-index": "^1.0.1" } }, + "d3-array": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.9.1.tgz", + "integrity": "sha512-Ob7RdOtkqsjx1NWyQHMFLtCSk6/aKTxDdC4ZIolX+O+mDD2RzrsYgAyc0WGAlfYFVELLSilS7w8BtE3PKM8bHg==" + }, "d3-dispatch": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-2.0.0.tgz", @@ -1898,6 +1903,14 @@ "d3-selection": "2" } }, + "d3-geo": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-2.0.1.tgz", + "integrity": "sha512-M6yzGbFRfxzNrVhxDJXzJqSLQ90q1cCyb3EWFZ1LF4eWOBYxFypw7I/NFVBNXKNqxv1bqLathhYvdJ6DC+th3A==", + "requires": { + "d3-array": ">=2.5" + } + }, "d3-selection": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-2.0.0.tgz", diff --git a/package.json b/package.json index eb87f9f..79dcbba 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "homepage": "https://github.com/zhenyanghua/MeasureTool-GoogleMaps-V3#readme", "dependencies": { "d3-drag": "^2.0.0", + "d3-geo": "^2.0.1", "d3-selection": "^2.0.0" }, "devDependencies": { diff --git a/rollup.config.js b/rollup.config.js index 932b74a..6f81ec4 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -46,4 +46,4 @@ const esm = { onwarn }; -export default [umd, esm]; +export default [esm]; diff --git a/src/geometry.js b/src/geometry.js index e6d6244..7ae226f 100644 --- a/src/geometry.js +++ b/src/geometry.js @@ -12,6 +12,20 @@ export class Geometry { return segments; } + get lingString() { + return Geometry.toLineString(this._nodes); + } + + static toLineString(points) { + return { + "type": "Feature", + "geometry": { + "type": "LineString", + "coordinates": points + } + } + } + constructor() { this._nodes = []; } @@ -31,4 +45,20 @@ export class Geometry { insertNode(i, point) { this._nodes.splice(i, 0, point); } + + static equals(segments1, segments2) { + if (segments1.length !== segments2.length) { + return false; + } + for (let i = 0; i < segments1.length; i++) { + if (segments1[i][0][0] !== segments2[i][0][0] || + segments1[i][0][1] !== segments2[i][0][1] || + segments1[i][1][0] !== segments2[i][1][0] || + segments1[i][1][1] !== segments2[i][1][1]) { + return false; + } + } + return true; + } + } diff --git a/src/helper.js b/src/helper.js index 6842e6d..04637e7 100644 --- a/src/helper.js +++ b/src/helper.js @@ -1,10 +1,12 @@ import { UnitTypeId } from './UnitTypeId'; +import { Geometry } from './geometry'; export default class Helper { constructor(options) { this._options = { unit: UnitTypeId.METRIC, }; Object.assign(this._options, options); + this._lastInterpolatedPoints = { segments: [], length: 0, points: [] }; this.init(); } @@ -196,7 +198,7 @@ export default class Helper { * @private */ static _interpolate(p1, p2, fraction) { - let point = google.maps.geometry.spherical.interpolate( + const point = google.maps.geometry.spherical.interpolate( new google.maps.LatLng(p1[1], p1[0]), new google.maps.LatLng(p2[1], p2[0]), fraction @@ -209,4 +211,62 @@ export default class Helper { // let y = m * x + b; // return [x, y]; } + + /** + * if last segment length (start with 0) adds current segment length is less than tick length, + * segment length = last segment length + segment length + * last segment length = segment length; + * move on to the next segment + * + * current point = compute the latlng using fraction (tick length - last segment length ) / current segment length + * between current segment start point and end point. + * segment length = segment length - (tick length - last segment length) + * + * While the given minimum tick length is less than the segment length, + * compute the latlng at the fraction between current point and the end point. + * push the latlng + * assign the latlng to current point. + * segment length subtracts tick length. + * + * @param segments + * @param length + * @param includeSegmentNodes + */ + interpolatePointsOnPath(segments, length, includeSegmentNodes = false) { + if (this._lastInterpolatedPoints.length === length && + Geometry.equals(this._lastInterpolatedPoints.segments, segments)) { + return this._lastInterpolatedPoints.points; + } + console.debug('interpolatePointsOnPath called') + if (segments.length === 0) return []; + let lastSegmentLength = 0, curPoint = segments[0][0], points = []; + for (let i = 0; i < segments.length; i++) { + if (includeSegmentNodes) points.push(segments[i][0]); + let segmentLength = this.computeLengthBetween(segments[i][0], segments[i][1]); + if (lastSegmentLength + segmentLength < length) { + segmentLength += lastSegmentLength; + lastSegmentLength = segmentLength; + continue; + } + curPoint = Helper._interpolate( + segments[i][0], + segments[i][1], + (length - lastSegmentLength) / segmentLength); + segmentLength -= (length - lastSegmentLength); + points.push(curPoint); + while(length < segmentLength) { + curPoint = Helper._interpolate(curPoint, segments[i][1], length / segmentLength); + points.push(curPoint); + segmentLength -= length; + lastSegmentLength = segmentLength; + } + } + if (includeSegmentNodes) points.push(segments[segments.length - 1][1]); + this._lastInterpolatedPoints = { + length, + segments, + points + }; + return points; + } } diff --git a/src/index.js b/src/index.js index 1ff50a2..ba9305f 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,6 @@ import { drag } from 'd3-drag'; import { select, selectAll } from 'd3-selection'; +import { geoPath, geoTransform, geoClipRectangle, geoProjection, geoMercator } from 'd3-geo'; import { Config } from './config'; import ContextMenu from './context-menu'; import Tooltip from './tooltip'; @@ -66,13 +67,14 @@ export default class MeasureTool { initialSegments: [], language: navigator ? navigator.language : 'en', invertColor: false, - ...options, + ...options }; this._map = map; this._map.setClickableIcons(false); this._id = Helper.makeId(4); this._events = new Map(); this._geometry = new Geometry(); + this._computeTickLength(); this._init(); } @@ -105,7 +107,7 @@ export default class MeasureTool { } this._helper = new Helper({ - unit: this._options.unit, + unit: this._options.unit }); this._initOverlay(); } @@ -275,6 +277,27 @@ export default class MeasureTool { .append('svg') .attr('class', `${Config.prefix}-svg-overlay`); + const { paths, gmPath } = this._getProjectedPath(); + this._tickPath = this._svgOverlay + .append('g') + .attr('class', 'tick-path'); + this._tickPath.selectAll('path').data(paths); + + this._ticks = this._svgOverlay + .append('g') + .attr('class', 'ticks'); + this._ticks.append('marker') + .attr('id', 'marker-small-ticks') + .attr('markerHeight', 3) + .attr('markerWidth', 1) + .attr('markerUnits', 'strokeWidth') + .attr('orient', 'auto') + .attr('refX', 0) + .attr('refY', 0) + .attr('viewBox', '-1 0 2 5') + .append('path') + .attr('d', 'M 0,0 m -1,0 L 1,0 L 1,5 L -1,5 Z'); + this._linesBase = this._svgOverlay.append('g').attr('class', 'base'); this._linesBase.selectAll('line').data(this._geometry.lines); @@ -328,6 +351,7 @@ export default class MeasureTool { this._updateCircles(); this._updateTouchCircles(); this._updateLine(); + this._updatePath(); if (this._options.showSegmentLength) { this._updateSegmentText(); } @@ -386,12 +410,12 @@ export default class MeasureTool { : getClass('cover-circle', this._options.invertColor) ) .attr('r', nodeTargetRadius) - .attr('cx', ([d]) => this._projectionUtility.latLngToSvgPoint(d)[0]) - .attr('cy', ([d]) => this._projectionUtility.latLngToSvgPoint(d)[1]) - .on('mouseover', function (event, [d, i]) { + .attr('cx', ([d]) => this._projectionUtility.lngLatToSvgPoint(d)[0]) + .attr('cy', ([d]) => this._projectionUtility.lngLatToSvgPoint(d)[1]) + .on('mouseover', function(event, [d, i]) { self._onOverCircle(d, i, this); }) - .on('mouseout', function (event, [d, i]) { + .on('mouseout', function(event, [d, i]) { self._onOutCircle(d, i, this); }) .on('mousedown', () => this._hideTooltip()); @@ -402,12 +426,12 @@ export default class MeasureTool { .append('circle') .attr('class', getClass('cover-circle', this._options.invertColor)) .attr('r', nodeTargetRadius) - .attr('cx', ([d]) => this._projectionUtility.latLngToSvgPoint(d)[0]) - .attr('cy', ([d]) => this._projectionUtility.latLngToSvgPoint(d)[1]) - .on('mouseover', function (event, [d, i]) { + .attr('cx', ([d]) => this._projectionUtility.lngLatToSvgPoint(d)[0]) + .attr('cy', ([d]) => this._projectionUtility.lngLatToSvgPoint(d)[1]) + .on('mouseover', function(event, [d, i]) { self._onOverCircle(d, i, this); }) - .on('mouseout', function (event, [d, i]) { + .on('mouseout', function(event, [d, i]) { self._onOutCircle(d, i, this); }) .on('mousedown', () => this._hideTooltip()); @@ -429,19 +453,19 @@ export default class MeasureTool { : getClass('touch-circle', this._options.invertColor) ) .attr('r', touchTargetRadius) - .attr('cx', ([d]) => this._projectionUtility.latLngToSvgPoint(d)[0]) - .attr('cy', ([d]) => this._projectionUtility.latLngToSvgPoint(d)[1]) - .on('mouseover', function (event, [d, i]) { + .attr('cx', ([d]) => this._projectionUtility.lngLatToSvgPoint(d)[0]) + .attr('cy', ([d]) => this._projectionUtility.lngLatToSvgPoint(d)[1]) + .on('mouseover', function(event, [d, i]) { self._onOverCircle(d, i, this); }) - .on('mouseout', function (event, [d, i]) { + .on('mouseout', function(event, [d, i]) { self._onOutCircle(d, i, this); }) - .on('touchstart', function (event, [d, i]) { + .on('touchstart', function(event, [d, i]) { event.preventDefault(); self._onOverCircle(d, i, this, true); }) - .on('touchend', function (event, [d, i]) { + .on('touchend', function(event, [d, i]) { event.preventDefault(); self._onOutCircle(d, i, this); }) @@ -454,19 +478,19 @@ export default class MeasureTool { .append('circle') .attr('class', getClass('touch-circle', this._options.invertColor)) .attr('r', touchTargetRadius) - .attr('cx', ([d]) => this._projectionUtility.latLngToSvgPoint(d)[0]) - .attr('cy', ([d]) => this._projectionUtility.latLngToSvgPoint(d)[1]) - .on('mouseover', function (event, [d, i]) { + .attr('cx', ([d]) => this._projectionUtility.lngLatToSvgPoint(d)[0]) + .attr('cy', ([d]) => this._projectionUtility.lngLatToSvgPoint(d)[1]) + .on('mouseover', function(event, [d, i]) { self._onOverCircle(d, i, this); }) - .on('mouseout', function (event, [d, i]) { + .on('mouseout', function(event, [d, i]) { self._onOutCircle(d, i, this); }) - .on('touchstart', function (event, [d, i]) { + .on('touchstart', function(event, [d, i]) { event.preventDefault(); self._onOverCircle(d, i, this, true); }) - .on('touchend', function (event, [d, i]) { + .on('touchend', function(event, [d, i]) { event.preventDefault(); self._onOutCircle(d, i, this); }) @@ -483,10 +507,10 @@ export default class MeasureTool { .selectAll('line') .data(this._geometry.lines) .attr('class', getClass('base-line', this._options.invertColor)) - .attr('x1', (d) => this._projectionUtility.latLngToSvgPoint(d[0])[0]) - .attr('y1', (d) => this._projectionUtility.latLngToSvgPoint(d[0])[1]) - .attr('x2', (d) => this._projectionUtility.latLngToSvgPoint(d[1])[0]) - .attr('y2', (d) => this._projectionUtility.latLngToSvgPoint(d[1])[1]) + .attr('x1', (d) => this._projectionUtility.lngLatToSvgPoint(d[0])[0]) + .attr('y1', (d) => this._projectionUtility.lngLatToSvgPoint(d[0])[1]) + .attr('x2', (d) => this._projectionUtility.lngLatToSvgPoint(d[1])[0]) + .attr('y2', (d) => this._projectionUtility.lngLatToSvgPoint(d[1])[1]) .each((d) => this._updateSegment(d)); linesBase.exit().remove(); @@ -494,10 +518,10 @@ export default class MeasureTool { .enter() .append('line') .attr('class', getClass('base-line', this._options.invertColor)) - .attr('x1', (d) => this._projectionUtility.latLngToSvgPoint(d[0])[0]) - .attr('y1', (d) => this._projectionUtility.latLngToSvgPoint(d[0])[1]) - .attr('x2', (d) => this._projectionUtility.latLngToSvgPoint(d[1])[0]) - .attr('y2', (d) => this._projectionUtility.latLngToSvgPoint(d[1])[1]) + .attr('x1', (d) => this._projectionUtility.lngLatToSvgPoint(d[0])[0]) + .attr('y1', (d) => this._projectionUtility.lngLatToSvgPoint(d[0])[1]) + .attr('x2', (d) => this._projectionUtility.lngLatToSvgPoint(d[1])[0]) + .attr('y2', (d) => this._projectionUtility.lngLatToSvgPoint(d[1])[1]) .each((d) => this._updateSegment(d)); let linesAux = this._linesAux @@ -506,17 +530,17 @@ export default class MeasureTool { .join('line') .datum((d, i) => [d, i]) .attr('class', 'aux-line') - .attr('x1', ([d]) => this._projectionUtility.latLngToSvgPoint(d[0])[0]) - .attr('y1', ([d]) => this._projectionUtility.latLngToSvgPoint(d[0])[1]) - .attr('x2', ([d]) => this._projectionUtility.latLngToSvgPoint(d[1])[0]) - .attr('y2', ([d]) => this._projectionUtility.latLngToSvgPoint(d[1])[1]); + .attr('x1', ([d]) => this._projectionUtility.lngLatToSvgPoint(d[0])[0]) + .attr('y1', ([d]) => this._projectionUtility.lngLatToSvgPoint(d[0])[1]) + .attr('x2', ([d]) => this._projectionUtility.lngLatToSvgPoint(d[1])[0]) + .attr('y2', ([d]) => this._projectionUtility.lngLatToSvgPoint(d[1])[1]); linesAux .on('mousemove', (event, [d]) => { let point = Helper.findTouchPoint( [ - this._projectionUtility.latLngToSvgPoint(d[0]), - this._projectionUtility.latLngToSvgPoint(d[1]), + this._projectionUtility.lngLatToSvgPoint(d[0]), + this._projectionUtility.lngLatToSvgPoint(d[1]) ], [event.offsetX, event.offsetY] ); @@ -537,16 +561,65 @@ export default class MeasureTool { .join('line') .datum((d, i) => [d, i]) .attr('class', 'aux-line') - .attr('x1', ([d]) => this._projectionUtility.latLngToSvgPoint(d[0])[0]) - .attr('y1', ([d]) => this._projectionUtility.latLngToSvgPoint(d[0])[1]) - .attr('x2', ([d]) => this._projectionUtility.latLngToSvgPoint(d[1])[0]) - .attr('y2', ([d]) => this._projectionUtility.latLngToSvgPoint(d[1])[1]); + .attr('x1', ([d]) => this._projectionUtility.lngLatToSvgPoint(d[0])[0]) + .attr('y1', ([d]) => this._projectionUtility.lngLatToSvgPoint(d[0])[1]) + .attr('x2', ([d]) => this._projectionUtility.lngLatToSvgPoint(d[1])[0]) + .attr('y2', ([d]) => this._projectionUtility.lngLatToSvgPoint(d[1])[1]); const lineDrag = this._lineDrag.selectAll('line').data([]); lineDrag.exit().remove(); } + _updatePath() { + const { paths, gmPath } = this._getProjectedPath(); + const tickPath = this._tickPath.selectAll('path') + .data(paths) + .attr('class', 'tick-path') + .attr('d', gmPath); + + tickPath.exit().remove(); + tickPath + .enter() + .append('path') + .attr('d', gmPath); + // .attr('marker-start', `url(#marker-small-ticks)`) + // .attr('marker-mid', `url(#marker-small-ticks)`) + // .attr('marker-end', `url(#marker-small-ticks)`); + } + + _getProjectedPath() { + const self = this; + this._computeTickLength(); + // FIXME - interpolate every points along a line that span across the world + // when the zoom level is large kills the performance. Need a different way + // of to partial interpolate the points that only within the bounds. + // todo - check if d3 projection handles geodesic projection for start/end + const points = this._helper.interpolatePointsOnPath( + this._geometry.lines, this._tickLength, true + ); + // console.debug('points count', points.length); + // const paths = [Geometry.toLineString(points)]; + const paths = [this._geometry.lingString]; + const gmTransform = geoTransform({ + point: function(lng, lat) { + // if (self._map.getBounds().contains({ lat, lng })) { + const [x, y] = self._projectionUtility.lngLatToSvgPoint([lng, lat]); + this.stream.point(x, y); + // } + } + }); + // const { east, north, south, west } = this._map.getBounds().toJSON(); + // const [x0, y0] = this._projectionUtility.lngLatToSvgPoint([south, west]); + // const [x1, y1] = this._projectionUtility.lngLatToSvgPoint([north, east]); + // const clip = geoClipRectangle(x0, y0, x1, y1); + const projection = geoMercator(); + // projection.postclip(clip); + const gmPath = geoPath(projection).projection(gmTransform); + + return { paths, gmPath }; + } + _updateSegmentText() { let text = this._segmentText .selectAll('text') @@ -558,8 +631,8 @@ export default class MeasureTool { .attr('text-anchor', 'middle') .attr('dominant-baseline', 'text-before-edge') .attr('transform', (d) => { - let p1 = this._projectionUtility.latLngToSvgPoint(d[0]); - let p2 = this._projectionUtility.latLngToSvgPoint(d[1]); + let p1 = this._projectionUtility.lngLatToSvgPoint(d[0]); + let p2 = this._projectionUtility.lngLatToSvgPoint(d[1]); return Helper.transformText(p1, p2); }) .text((d, i) => @@ -576,8 +649,8 @@ export default class MeasureTool { .attr('text-anchor', 'middle') .attr('dominant-baseline', 'text-before-edge') .attr('transform', (d) => { - let p1 = this._projectionUtility.latLngToSvgPoint(d[0]); - let p2 = this._projectionUtility.latLngToSvgPoint(d[1]); + let p1 = this._projectionUtility.lngLatToSvgPoint(d[0]); + let p2 = this._projectionUtility.lngLatToSvgPoint(d[1]); return Helper.transformText(p1, p2); }) .text((d, i) => @@ -594,14 +667,14 @@ export default class MeasureTool { .attr('class', (d, i) => i === 0 ? `${getClass( - 'node-measure-text', - this._options.invertColor - )} head-text` + 'node-measure-text', + this._options.invertColor + )} head-text` : getClass('node-measure-text', this._options.invertColor) ) .attr('text-anchor', 'middle') .attr('dominant-baseline', 'text-after-edge') - .attr('x', (d) => this._projectionUtility.latLngToSvgPoint(d)[0]) + .attr('x', (d) => this._projectionUtility.lngLatToSvgPoint(d)[0]) .attr('y', this._transformNodeTextY.bind(this)) .text((d, i) => { let len = this._helper.computePathLength( @@ -619,14 +692,14 @@ export default class MeasureTool { .attr('class', (d, i) => i === 0 ? `${getClass( - 'node-measure-text', - this._options.invertColor - )} head-text` + 'node-measure-text', + this._options.invertColor + )} head-text` : getClass('node-measure-text', this._options.invertColor) ) .attr('text-anchor', 'middle') .attr('dominant-baseline', 'text-after-edge') - .attr('x', (d) => this._projectionUtility.latLngToSvgPoint(d)[0]) + .attr('x', (d) => this._projectionUtility.lngLatToSvgPoint(d)[0]) .attr('y', this._transformNodeTextY.bind(this)) .text((d, i) => { let len = this._helper.computePathLength( @@ -651,7 +724,7 @@ export default class MeasureTool { if (this._options.tooltip && !isTouch) { this._tooltip.show( - this._projectionUtility.latLngToContainerPoint(d), + this._projectionUtility.lngLatToContainerPoint(d), i === 0 ? Config.tooltipText2(this._options.language) : Config.tooltipText1(this._options.language) @@ -672,7 +745,7 @@ export default class MeasureTool { let self = this; let isDragged = false; - let circleDrag = drag().on('drag', function (event, [, i]) { + let circleDrag = drag().on('drag', function(event, [, i]) { isDragged = true; self._dragging = true; @@ -692,13 +765,13 @@ export default class MeasureTool { ); }); - circleDrag.on('start', function (event) { + circleDrag.on('start', function(event) { event.sourceEvent.stopPropagation(); select(this).raise().attr('r', nodeTargetExpandRadius); self._disableMapScroll(); }); - circleDrag.on('end', function (event, [d, i]) { + circleDrag.on('end', function(event, [d, i]) { self._enableMapScroll(); if (!isDragged) { if (i > 0) { @@ -751,10 +824,10 @@ export default class MeasureTool { .enter() .append('line') .attr('class', getClass('base-line', this._options.invertColor)) - .attr('x1', (d) => this._projectionUtility.latLngToSvgPoint(d[0])[0]) - .attr('y1', (d) => this._projectionUtility.latLngToSvgPoint(d[0])[1]) - .attr('x2', (d) => this._projectionUtility.latLngToSvgPoint(d[1])[0]) - .attr('y2', (d) => this._projectionUtility.latLngToSvgPoint(d[1])[1]); + .attr('x1', (d) => this._projectionUtility.lngLatToSvgPoint(d[0])[0]) + .attr('y1', (d) => this._projectionUtility.lngLatToSvgPoint(d[0])[1]) + .attr('x2', (d) => this._projectionUtility.lngLatToSvgPoint(d[1])[0]) + .attr('y2', (d) => this._projectionUtility.lngLatToSvgPoint(d[1])[1]); this._linesBase.selectAll('line').style('display', 'none'); this._linesAux.selectAll('line').style('display', 'none'); @@ -839,7 +912,7 @@ export default class MeasureTool { .select(`text:nth-child(${index + 1})`) .attr('transform', (d) => { let p1 = [event.x, event.y]; - let p2 = this._projectionUtility.latLngToSvgPoint(d[1]); + let p2 = this._projectionUtility.lngLatToSvgPoint(d[1]); return Helper.transformText(p1, p2); }) .text((d) => @@ -855,7 +928,7 @@ export default class MeasureTool { this._segmentText .select(`text:nth-child(${index})`) .attr('transform', (d) => { - let p1 = this._projectionUtility.latLngToSvgPoint(d[0]); + let p1 = this._projectionUtility.lngLatToSvgPoint(d[0]); let p2 = [event.x, event.y]; return Helper.transformText(p1, p2); }) @@ -878,7 +951,7 @@ export default class MeasureTool { let offset; if ( index > 0 && - this._projectionUtility.latLngToSvgPoint( + this._projectionUtility.lngLatToSvgPoint( this._geometry.nodes[index - 1] )[1] < event.y ) { @@ -892,13 +965,13 @@ export default class MeasureTool { let offset; if ( index + 1 > 0 && - event.y < this._projectionUtility.latLngToSvgPoint(d)[1] + event.y < this._projectionUtility.lngLatToSvgPoint(d)[1] ) { offset = 23; } else { offset = -7; } - return this._projectionUtility.latLngToSvgPoint(d)[1] + offset; + return this._projectionUtility.lngLatToSvgPoint(d)[1] + offset; }); let followingNodes = this._nodeText .selectAll('text') @@ -907,7 +980,7 @@ export default class MeasureTool { let len = this._helper.computePathLength([ ...this._geometry.nodes.slice(0, index), this._projectionUtility.svgPointToLatLng([event.x, event.y]), - ...this._geometry.nodes.slice(index + 1, index + 1 + i), + ...this._geometry.nodes.slice(index + 1, index + 1 + i) ]); if (index + i === this._geometry.nodes.length - 1) { this._length = len; @@ -940,12 +1013,12 @@ export default class MeasureTool { _disableMapScroll() { this._zoomControl = !!document.querySelector( - "button[aria-label='Zoom in']" + 'button[aria-label=\'Zoom in\']' ); this._map.setOptions({ scrollwheel: false, gestureHandling: 'none', - zoomControl: false, + zoomControl: false }); } @@ -953,7 +1026,7 @@ export default class MeasureTool { this._map.setOptions({ scrollwheel: true, gestureHandling: 'auto', - zoomControl: this._zoomControl, + zoomControl: this._zoomControl }); } @@ -964,7 +1037,7 @@ export default class MeasureTool { } else { offset = -7; } - return this._projectionUtility.latLngToSvgPoint(d)[1] + offset; + return this._projectionUtility.lngLatToSvgPoint(d)[1] + offset; } _updateArea(i, pointToCompare) { @@ -983,9 +1056,9 @@ export default class MeasureTool { offset > tolerance ? 0 : this._helper.computeArea([ - pointToCompare, - ...this._geometry.nodes.slice(1, n - 1), - ]); + pointToCompare, + ...this._geometry.nodes.slice(1, n - 1) + ]); } else if (i === n - 1) { offset = this._helper.computeLengthBetween( pointToCompare, @@ -1004,10 +1077,10 @@ export default class MeasureTool { offset > tolerance ? 0 : this._helper.computeArea([ - ...this._geometry.nodes.slice(0, i), - pointToCompare, - ...this._geometry.nodes.slice(i + 1), - ]); + ...this._geometry.nodes.slice(0, i), + pointToCompare, + ...this._geometry.nodes.slice(i + 1) + ]); } else { offset = this._helper.computeLengthBetween( this._geometry.nodes[0], @@ -1077,8 +1150,15 @@ export default class MeasureTool { area: this.area, areaText: this.areaText, segments: this.segments, - points: this.points, - }, + points: this.points + } }; } + + _computeTickLength() { + // const metersPerPixel = 156543.03392 * Math.cos(this._map.getCenter().lat() * Math.PI / 180) + // / Math.pow(2, this._map.getZoom()); + // this._tickLength = metersPerPixel * 30; + this._tickLength = 1600000 / Math.pow(2, this._map.getZoom() - 1); + } } diff --git a/src/index.scss b/src/index.scss index 75831d0..1e8788a 100644 --- a/src/index.scss +++ b/src/index.scss @@ -26,6 +26,22 @@ $alpha-black: rgba(0, 0, 0, 1); pointer-events: none; } +.tick-path { + fill: none; + stroke: red; + stroke-width: 6px; + pointer-events: none; +} + +.ticks { + pointer-events: none; + marker { + path { + fill: blue; + } + } +} + .base-line { fill: none; stroke: black; diff --git a/src/projection-utility.js b/src/projection-utility.js index 5f0c9fa..ac37d38 100644 --- a/src/projection-utility.js +++ b/src/projection-utility.js @@ -8,7 +8,7 @@ export default class ProjectionUtility { this._projection = projection; } - latLngToSvgPoint(coords) { + lngLatToSvgPoint(coords) { let rate = this._options.offsetRate / 2; let latLng = new google.maps.LatLng(coords[1], coords[0]); let svgPoint = this._projection.fromLatLngToDivPixel(latLng); @@ -29,7 +29,7 @@ export default class ProjectionUtility { ); } - latLngToContainerPoint(coords) { + lngLatToContainerPoint(coords) { return this._projection.fromLatLngToContainerPixel( new google.maps.LatLng(coords[1], coords[0]) );