UNPKG

@mint-ui/map

Version:

- React map library - Control various map with one interface - Google, Naver, Kakao map supported now - Typescript supported - Canvas marker supported

1,701 lines (1,392 loc) 205 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('tslib'), require('react'), require('classnames/bind'), require('style-inject'), require('uuid'), require('@mint-ui/tools'), require('react-dom')) : typeof define === 'function' && define.amd ? define(['exports', 'tslib', 'react', 'classnames/bind', 'style-inject', 'uuid', '@mint-ui/tools', 'react-dom'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["@mint-ui/map"] = {}, global.tslib, global.React, global.classNames, global.styleInject, global.uuid, global.tools, global.reactDom)); })(this, (function (exports, tslib, React, classNames, styleInject, uuid, tools, reactDom) { 'use strict'; function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var React__default = /*#__PURE__*/_interopDefaultLegacy(React); var classNames__default = /*#__PURE__*/_interopDefaultLegacy(classNames); var styleInject__default = /*#__PURE__*/_interopDefaultLegacy(styleInject); var css_248z$2 = ".MintMap-module_loading-point-container__znk6l {\n display: flex;\n justify-content: center;\n align-items: center;\n width: 100%;\n height: 100%;\n}\n\n.MintMap-module_ani-blink__K89JK {\n animation: MintMap-module_blink__mqfeV infinite 0.6s;\n}\n\n@keyframes MintMap-module_blink__mqfeV {\n 0% {\n opacity: 1;\n }\n 50% {\n opacity: 0.3;\n }\n 100% {\n opacity: 1;\n }\n}\n.MintMap-module_ani-fade-in__lpHuy {\n animation: MintMap-module_fade-in__jHpv1 1s;\n}\n\n@keyframes MintMap-module_fade-in__jHpv1 {\n 0% {\n opacity: 0;\n }\n 100% {\n opacity: 1;\n }\n}\n.MintMap-module_ani-fade-out__5-esw {\n animation: MintMap-module_fade-out__CIjGe 1s;\n}\n\n@keyframes MintMap-module_fade-out__CIjGe {\n 0% {\n opacity: 1;\n }\n 100% {\n opacity: 0;\n }\n}\n.MintMap-module_ani-expansion__S2vOZ {\n animation: MintMap-module_expansion__WMo5- ease 0.6s;\n}\n\n@keyframes MintMap-module_expansion__WMo5- {\n 0% {\n width: 0%;\n }\n 100% {\n width: 100%;\n }\n}"; var styles$2 = {"loading-point-container":"MintMap-module_loading-point-container__znk6l","ani-blink":"MintMap-module_ani-blink__K89JK","blink":"MintMap-module_blink__mqfeV","ani-fade-in":"MintMap-module_ani-fade-in__lpHuy","fade-in":"MintMap-module_fade-in__jHpv1","ani-fade-out":"MintMap-module_ani-fade-out__5-esw","fade-out":"MintMap-module_fade-out__CIjGe","ani-expansion":"MintMap-module_ani-expansion__S2vOZ","expansion":"MintMap-module_expansion__WMo5-"}; styleInject__default["default"](css_248z$2); var PositionMirror$1 = /** @class */ function () { function PositionMirror(lat, lng) { this.lat = lat; this.lng = lng; } return PositionMirror; }(); var GeoCalulator = /** @class */ function () { function GeoCalulator() {} GeoCalulator.computeDistanceKiloMeter = function (pos1, pos2) { var dLat = this.deg2rad(pos2.lat - pos1.lat); var dLon = this.deg2rad(pos2.lng - pos1.lng); var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(this.deg2rad(pos1.lat)) * Math.cos(this.deg2rad(pos2.lat)) * Math.sin(dLon / 2) * Math.sin(dLon / 2); var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); var d = this.EARTH_EQUATORIAL_RADIUS_KM * c; // Distance in km return d; }; GeoCalulator.deg2rad = function (deg) { return deg * (Math.PI / 180); }; GeoCalulator.convertMeterToLatitudeValue = function (meter) { return meter * this.LATITUDE_POSITION_VALUE_PER_METER; }; GeoCalulator.convertLatitudeToMeterValue = function (lat) { return lat * this.METER_VALUE_PER_LATITUDE; }; GeoCalulator.convertLongitudeToMeterValue = function (lat, lng) { return lng * this.calculateLongitudeValueWithLatitudeInMeter(lat); }; GeoCalulator.getCacheUnitOfLongitudeValueWithLatitudeInMeter = function (lat) { return lat.toFixed(2); }; GeoCalulator.getCacheOfLongitudeValueWithLatitudeInMeter = function (latUnit) { return this.CACHE_OF_LNG_PER_METER.get(latUnit); }; GeoCalulator.setCacheOfLongitudeValueWithLatitudeInMeter = function (latUnit, lngValue) { if (this.CACHE_OF_LNG_PER_METER.size > 10) { this.CACHE_OF_LNG_PER_METER.clear(); } this.CACHE_OF_LNG_PER_METER.set(latUnit, lngValue); }; GeoCalulator.calculateLongitudeValueWithLatitudeInMeter = function (lat) { // const t = Date.now() // Cache check var latUnit = this.getCacheUnitOfLongitudeValueWithLatitudeInMeter(lat); var fromCache = this.getCacheOfLongitudeValueWithLatitudeInMeter(latUnit); if (fromCache !== undefined) { // console.log(`cache hit!! ${Date.now() - t} ms`, fromCache, latUnit, this.CACHE_OF_LNG_PER_METER.size); return fromCache; } // Convert latitude and longitude to radians var latRad = lat * Math.PI / 180; // Calculate Earth's radius at the given latitude var radius = this.EARTH_EQUATORIAL_RADIUS * Math.sqrt(1 - Math.pow(this.EARTH_ECCENTRICITY * Math.sin(latRad), 2)); // Calculate the length of one degree of longitude in meters var distance = 2 * Math.PI * radius * Math.cos(latRad) / 360; // Cache set this.setCacheOfLongitudeValueWithLatitudeInMeter(latUnit, distance); // console.log(`calculated ${Date.now() - t} ms`) return distance; }; GeoCalulator.computeNextPositionAndDistances = function (context) { var pos1 = context.pos1, pos2 = context.pos2, prevPos2 = context.prevPos2, velocityKmh = context.velocityKmh, prevVelocityKmh = context.prevVelocityKmh, elapsedTimeMs = context.elapsedTimeMs; // console.log('velocityKmh / elapsedTimeMs',velocityKmh , elapsedTimeMs); //총 가야할 거리 (km) if (pos2 !== prevPos2) { //목표가 바뀌면 거리 및 비율 재계산 context.totalDistance = this.computeDistanceKiloMeter(pos1, pos2); context.currDistance = 0; context.prevPos2 = pos2; } var totalDistance = context.totalDistance; // console.log('totalDistance', totalDistance); //ms 속으로 환산 if (velocityKmh !== prevVelocityKmh) { //속도가 바뀌면 재계산 context.vPerMs = velocityKmh / this.MS_FROM_HOUR; context.prevVelocityKmh = velocityKmh; } var vPerMs = context.vPerMs; //console.log('vPerMs', vPerMs); //실제 가는 거리 계산 var nextDistance = context.distanceRemain ? context.distanceRemain : context.currDistance + elapsedTimeMs * vPerMs; //console.log('nextDistance', nextDistance); //목표점까지 이동 후에도 남는 거리 context.currDistance = nextDistance; if (totalDistance < context.currDistance) { //이동 거리가 현재 목표점을 넘어가는 경우 context.distanceRemain = context.currDistance - totalDistance; context.nextPos = pos2; return context; } else { context.distanceRemain = 0; } //각 축으로 나가야할 비율 var ratio = nextDistance / totalDistance; //console.log('ratio', ratio); //방향값 체크 var latCalib = pos2.lat > pos1.lat ? 1 : -1; var lngCalib = pos2.lng > pos1.lng ? 1 : -1; //각 축에 보정된 새로운 지점 리턴 var newPos = new PositionMirror$1(pos1.lat + (pos2.lat - pos1.lat) * ratio, pos1.lng + (pos2.lng - pos1.lng) * ratio); if ((latCalib === 1 && pos2.lat <= newPos.lat || latCalib === -1 && pos2.lat >= newPos.lat) && (lngCalib === 1 && pos2.lng <= newPos.lng || lngCalib === -1 && pos2.lng >= newPos.lng)) { newPos = pos2; } // console.log('newPos', newPos); //console.log('==============================================================\n'); context.nextPos = newPos; return context; }; GeoCalulator.EARTH_EQUATORIAL_RADIUS = 6378137; //meter (6,378,137 m) GeoCalulator.EARTH_EQUATORIAL_RADIUS_KM = GeoCalulator.EARTH_EQUATORIAL_RADIUS / 1000; GeoCalulator.EARTH_ECCENTRICITY = 0.08181919; GeoCalulator.METER_VALUE_PER_LATITUDE = 110.32 * 1000; //위도 기준 1도는 110.32km GeoCalulator.LATITUDE_POSITION_VALUE_PER_METER = 1 / GeoCalulator.METER_VALUE_PER_LATITUDE; GeoCalulator.CACHE_OF_LNG_PER_METER = new Map(); GeoCalulator.MS_FROM_HOUR = 60 * 60 * 1000; return GeoCalulator; }(); var LinePoints = /** @class */ function () { function LinePoints(x1, y1, x2, y2) { this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; } return LinePoints; }(); var PositionMirror = /** @class */ function () { function PositionMirror(lat, lng) { this.lat = lat; this.lng = lng; } return PositionMirror; }(); var PolygonCalculator = /** @class */ function () { function PolygonCalculator() {} PolygonCalculator.getRegionInfo = function (positions) { var maxX, minX, maxY, minY; for (var _i = 0, positions_1 = positions; _i < positions_1.length; _i++) { var pos = positions_1[_i]; if (maxX === undefined || pos.lat > maxX) { maxX = pos.lat; } if (minX === undefined || pos.lat < minX) { minX = pos.lat; } if (maxY === undefined || pos.lng > maxY) { maxY = pos.lng; } if (minY === undefined || pos.lng < minY) { minY = pos.lng; } } return { maxLat: maxX, minLat: minX, maxLng: maxY, minLng: minY, centerLat: minX && maxX ? minX + (maxX - minX) / 2 : undefined, centerLng: minY && maxY ? minY + (maxY - minY) / 2 : undefined }; }; PolygonCalculator.getRegionStart = function (positions) { var info = this.getRegionInfo(positions); if (info.minLat && info.minLng) { return new PositionMirror(info.minLat, info.minLng); } throw new Error('Calculate RegionStart Error!!!'); }; PolygonCalculator.getRegionEnd = function (positions) { var info = this.getRegionInfo(positions); if (info.maxLat && info.maxLng) { return new PositionMirror(info.maxLat, info.maxLng); } throw new Error('Calculate RegionEnd Error!!!'); }; PolygonCalculator.getCenter = function (positions) { var info = this.getRegionInfo(positions); if (info.centerLat && info.centerLng) { return new PositionMirror(info.centerLat, info.centerLng); } throw new Error('Calculate Center Error!!!'); }; PolygonCalculator.intersects = function (positions1, positions2) { var lines = []; for (var i = 0; i < positions2.length - 1; i++) { lines.push(this.convertPositionToPoints(positions2[i], positions2[i + 1])); } for (var i = 0; i < positions1.length - 1; i++) { var targetLinePoints = this.convertPositionToPoints(positions1[i], positions1[i + 1]); if (this.findCrossPoint(lines, targetLinePoints)) { return true; } } return false; }; PolygonCalculator.getIncludedPositions = function (polygon, position) { var targets = Array.isArray(position) ? position : [position]; var result = []; var maxX = Math.max.apply(Math, polygon.map(function (pos) { return pos.lng; })) + 1; var lines = this.convertPolygonToLinePoints(polygon); for (var _i = 0, targets_1 = targets; _i < targets_1.length; _i++) { var target = targets_1[_i]; //x 축으로 긴 직선과 폴리곤 path 와의 교차 수 비교 var tempLine = this.convertPositionToPoints(target, new PositionMirror(target.lat, maxX)); var crossPoints = this.getCrossPointAll(lines, tempLine); // console.log('crossPoints',crossPoints); if (crossPoints.length > 0 && crossPoints.length % 2 != 0) { result.push(target); } } return result; }; PolygonCalculator.convertPolygonToLinePoints = function (polygon) { var lines = []; for (var i = 0; i < polygon.length; i++) { lines.push(this.convertPositionToPoints(polygon[i], polygon[i + 1 === polygon.length ? 0 : i + 1])); } return lines; }; PolygonCalculator.convertPositionToPoints = function (pos1, pos2) { return new LinePoints(pos1.lng, pos1.lat, pos2.lng, pos2.lat); }; //두 직선의 교점 구하기 PolygonCalculator.getCrossPoint = function (sr, tr) { var p1 = (sr.x1 - sr.x2) * (tr.y1 - tr.y2) - (sr.y1 - sr.y2) * (tr.x1 - tr.x2); //분모로 사용됨 // p1 ==0 이면 평행선 또는 일치 이므로 제외(분모) if (p1 != 0) { var x = ((sr.x1 * sr.y2 - sr.y1 * sr.x2) * (tr.x1 - tr.x2) - (sr.x1 - sr.x2) * (tr.x1 * tr.y2 - tr.y1 * tr.x2)) / p1; var y = ((sr.x1 * sr.y2 - sr.y1 * sr.x2) * (tr.y1 - tr.y2) - (sr.y1 - sr.y2) * (tr.x1 * tr.y2 - tr.y1 * tr.x2)) / p1; if (this.toFixedPosition((x - sr.x1) * (x - sr.x2)) <= 0 && this.toFixedPosition((y - sr.y1) * (y - sr.y2)) <= 0 //교점이 1선분 위에 있고 && this.toFixedPosition((x - tr.x1) * (x - tr.x2)) <= 0 && this.toFixedPosition((y - tr.y1) * (y - tr.y2)) <= 0 //교점이 2선분 위에 있을경우 ) { return { x: x, y: y }; } } }; PolygonCalculator.toFixedPosition = function (n) { return Number(n.toFixed(15)); }; PolygonCalculator.getCrossPointAll = function (sr, tr) { var result = []; for (var _i = 0, sr_1 = sr; _i < sr_1.length; _i++) { var tmp = sr_1[_i]; var p = this.getCrossPoint(tmp, tr); if (p) { result.push(p); } } return result; }; PolygonCalculator.findCrossPoint = function (sr, tr) { for (var _i = 0, sr_2 = sr; _i < sr_2.length; _i++) { var tmp = sr_2[_i]; //교점이 있으면 true if (this.getCrossPoint(tmp, tr)) { return true; } } return false; }; PolygonCalculator.simplifyPoints = function (polygon, tolerance, _lastRepeated) { return this.simplify(this.pathCleaning(polygon), tolerance !== undefined ? tolerance : this.TOLERANCE_NAVER_STYLE); }; PolygonCalculator.pathCleaning = function (polygon) { if (polygon.length < 3) { return polygon; } var main = polygon[0]; var delCount = 0; for (var i = polygon.length - 1; i >= 0; i--) { if (main.equals(polygon[i])) { delCount += 1; } else { break; } } delCount > 0 && polygon.splice(polygon.length - delCount, delCount); var out = []; out.push.apply(out, polygon); return out; }; PolygonCalculator.simplify = function (points, tolerance) { if (points.length <= 2) { return points; } var dMax = 0; var index = 0; // Find the point with the maximum distance from the line segment var denominator = this.perpendicularDistanceDenominator(points[0], points[points.length - 1]); for (var i = 1; i < points.length - 1; i++) { var d = this.perpendicularDistance(points[i], points[0], points[points.length - 1], denominator); if (d > dMax) { dMax = d; index = i; } } // If the maximum distance is greater than the tolerance, recursively simplify if (dMax > tolerance) { var left = this.simplify(points.slice(0, index + 1), tolerance); var right = this.simplify(points.slice(index), tolerance); // Concatenate the simplified left and right segments return left.slice(0, left.length - 1).concat(right); } else { // If the maximum distance is less than or equal to the tolerance, return the endpoints return [points[0], points[points.length - 1]]; } }; PolygonCalculator.perpendicularDistanceDenominator = function (lineStart, lineEnd) { var x1 = lineStart.x, y1 = lineStart.y; var x2 = lineEnd.x, y2 = lineEnd.y; return Math.sqrt(Math.pow(y2 - y1, 2) + Math.pow(x2 - x1, 2)); }; // Calculate the perpendicular distance from a point to a line segment PolygonCalculator.perpendicularDistance = function (point, lineStart, lineEnd, denominator) { var x = point.x; var y = point.y; var x1 = lineStart.x; var y1 = lineStart.y; var x2 = lineEnd.x; var y2 = lineEnd.y; return Math.abs((y2 - y1) * x - (x2 - x1) * y + x2 * y1 - y2 * x1) / denominator; }; PolygonCalculator.calculatePolygonSize = function (polygon, innerPolygons) { var _this = this; var outer = this.calculatePolygonSizeMain(polygon); var inner = 0; innerPolygons && innerPolygons.map(function (innerPolygon) { inner += _this.calculatePolygonSizeMain(innerPolygon); }); return outer - inner; }; PolygonCalculator.calculatePolygonSizeMain = function (polygon) { var vertices = polygon.map(function (pos) { return { x: GeoCalulator.convertLongitudeToMeterValue(pos.lat, pos.lng), y: GeoCalulator.convertLatitudeToMeterValue(pos.lat) }; }); var n = vertices.length; var sum = 0; for (var i = 0; i < n; i++) { var currentVertex = vertices[i]; var nextVertex = vertices[(i + 1) % n]; sum += currentVertex.x * nextVertex.y - currentVertex.y * nextVertex.x; } var area = Math.abs(sum) / 2; return area; }; PolygonCalculator.TOLERANCE_NAVER_STYLE = 1; PolygonCalculator.TOLERANCE_GOOGLE_STYLE = 1; return PolygonCalculator; }(); /** * 좌표값 * @description 위도/경도, DOM 상의 X/Y 좌표 */ var Position = /** @class */ function () { function Position(lat, lng) { /** * 위도 * @description 위도(latitude) */ this.lat = 0; /** * 경도 * @description 경도(longitude) */ this.lng = 0; this.lat = lat; this.lng = lng; } Position.equals = function (pos1, pos2) { return pos1.lat === pos2.lat && pos1.lng === pos2.lng; }; return Position; }(); var Bounds = /** @class */ function () { function Bounds(nw, se, ne, sw) { if (!(nw && se || ne && sw)) { throw new Error('nw/se or ne/sw needed'); } //@ts-ignore this.nw = nw; this.se = se; this.ne = ne; this.sw = sw; if (nw && se) { this.covertNWSEtoNESW(nw, se); } else if (ne && sw) { this.covertNESWtoNWSE(ne, sw); } } Bounds.fromNWSE = function (nw, se) { return new Bounds(nw, se, undefined, undefined); }; Bounds.fromNESW = function (ne, sw) { return new Bounds(undefined, undefined, ne, sw); }; Bounds.prototype.covertNWSEtoNESW = function (nw, se) { this.ne = new Position(nw.lat, se.lng); this.sw = new Position(se.lat, nw.lng); }; Bounds.prototype.covertNESWtoNWSE = function (ne, sw) { this.nw = new Position(ne.lat, sw.lng); this.se = new Position(sw.lat, ne.lng); }; Bounds.prototype.getCenter = function () { return new Position(this.se.lat + (this.nw.lat - this.se.lat) / 2, this.nw.lng + (this.se.lng - this.nw.lng) / 2); }; Bounds.prototype.includesPosition = function (pos) { return this.nw.lng < pos.lng && this.se.lng > pos.lng && this.se.lat < pos.lat && this.nw.lat > pos.lat; }; Bounds.prototype.getIncludedPositions = function (positions) { var result = []; for (var _i = 0, positions_1 = positions; _i < positions_1.length; _i++) { var pos = positions_1[_i]; if (this.includesPosition(pos)) { result.push(pos); } } return result; }; Bounds.prototype.includes = function (positions) { positions = Array.isArray(positions) ? positions : [positions]; for (var _i = 0, positions_2 = positions; _i < positions_2.length; _i++) { var pos = positions_2[_i]; if (!this.includesPosition(pos)) { return false; } } return true; }; Bounds.prototype.includesOnlyOnePoint = function (positions) { for (var _i = 0, positions_3 = positions; _i < positions_3.length; _i++) { var pos = positions_3[_i]; if (this.includesPosition(pos)) { return true; } } return false; }; Bounds.prototype.intersects = function (positions) { return PolygonCalculator.intersects([this.nw, this.sw, this.se, this.ne, this.nw], positions); }; return Bounds; }(); /** * DOM 상에서의 좌표를 표현 (픽셀을 나타내는 숫자) */ var Offset = /** @class */ function () { /** * DOM 상에서의 좌표를 표현 (픽셀을 나타내는 숫자) */ function Offset(x, y) { this.x = x; this.y = y; } Offset.prototype.equals = function (other) { return other && this.x === other.x && this.y === other.y; }; return Offset; }(); var Spacing = /** @class */ function () { function Spacing() {} return Spacing; }(); var MintMapControllerContext = React.createContext(null); function MintMapProvider(_a) { var controller = _a.controller, children = _a.children; return React__default["default"].createElement(MintMapControllerContext.Provider, { value: controller }, children); } function useMintMapController() { var controller = React.useContext(MintMapControllerContext); if (controller === null) { throw new Error('controller is not initialized'); } return controller; } var css_248z$1 = ".MintMapCore-module_mint-map-root__SMfwn {\n position: relative;\n height: 100%;\n overflow: hidden;\n}\n\n.MintMapCore-module_mint-map-container__8MIIr {\n height: 100%;\n}"; var styles$1 = {"mint-map-root":"MintMapCore-module_mint-map-root__SMfwn","mint-map-container":"MintMapCore-module_mint-map-container__8MIIr"}; styleInject__default["default"](css_248z$1); var cn$3 = classNames__default["default"].bind(styles$1); function MintMapCore(_a) { var _this = this; var onLoad = _a.onLoad, _b = _a.visible, visible = _b === void 0 ? true : _b, zoomLevel = _a.zoomLevel, center = _a.center, _c = _a.centerMoveWithPanning, centerMoveWithPanning = _c === void 0 ? false : _c, children = _a.children; //controller var controller = useMintMapController(); //맵 초기화 var elementRef = React.useRef(null); var _d = React.useState(false), mapInitialized = _d[0], setMapInitialized = _d[1]; var currMapInitialized = React.useRef(false); React.useEffect(function () { (function () { return tslib.__awaiter(_this, void 0, void 0, function () { var map_1; return tslib.__generator(this, function (_a) { switch (_a.label) { case 0: if (!(elementRef && elementRef.current)) return [3 /*break*/ , 2]; return [4 /*yield*/ , controller.initializingMap(elementRef.current)]; case 1: map_1 = _a.sent(); if (!currMapInitialized.current) { currMapInitialized.current = true; //onload callback (setTimeout 으로 맵이 초기화 될 텀을 준다. 특히 google map..) setTimeout(function () { // console.log('setMapInitialized true'); setMapInitialized(true); onLoad && onLoad(map_1, controller); }, 100); } _a.label = 2; case 2: return [2 /*return*/ ]; } }); }); })(); }, [controller, elementRef]); //줌레벨 React.useEffect(function () { if (zoomLevel && controller && mapInitialized) { var prevZoomLevel = controller === null || controller === void 0 ? void 0 : controller.getZoomLevel(); if (prevZoomLevel !== zoomLevel) { controller === null || controller === void 0 ? void 0 : controller.setZoomLevel(zoomLevel); } } }, [zoomLevel]); //센터 React.useEffect(function () { if (center && controller && mapInitialized) { var prevCenter = controller.getCenter(); if (!Position.equals(prevCenter, center)) { centerMoveWithPanning ? controller === null || controller === void 0 ? void 0 : controller.panningTo(center) : controller === null || controller === void 0 ? void 0 : controller.setCenter(center); } } }, [center]); return React__default["default"].createElement("div", { className: cn$3('mint-map-root') }, mapInitialized && children, React__default["default"].createElement("div", { className: cn$3('mint-map-container'), style: { visibility: visible ? 'inherit' : 'hidden' }, ref: elementRef })); } var AnimationPlayer = /** @class */ function () { function AnimationPlayer(drawFunction, fps) { this.prevtime = 0; this.elapsedTime = 0; this.fps = null; this.baseDrawGapTime = null; this.deltaTime = 0; this.playing = false; this.draw = drawFunction; this.fps = fps || null; if (fps !== undefined) { this.baseDrawGapTime = 1000 / fps; } this.init(); } AnimationPlayer.prototype.init = function () { this.deltaTime = 0; this.prevtime = 0; this.elapsedTime = 0; this.playing = false; }; AnimationPlayer.prototype.start = function () { this.init(); this.resume(); }; AnimationPlayer.prototype.stop = function () { this.playing = false; }; AnimationPlayer.prototype.resume = function () { this.playing = true; //@ts-ignore window.requestAnimationFrame(this.makeFrame.bind(this)); }; AnimationPlayer.prototype.makeFrame = function (timestamp) { //frame 간 시간 변화 if (this.prevtime === 0) { this.prevtime = timestamp; } this.deltaTime += timestamp - this.prevtime; //정해진 시간이 없거나, 정해진 시간이 지났으면 draw 호출 this.prevtime = timestamp; if (!this.baseDrawGapTime || this.baseDrawGapTime <= this.deltaTime) { //다음 루프 준비 this.elapsedTime += this.deltaTime; //draw 콜백에서 stop 신호오면 멈춤 var stopFlag = this.draw(this.deltaTime, this.elapsedTime); //delta 초기화 this.deltaTime = 0; if (stopFlag) { this.stop(); } } if (this.playing) { //@ts-ignore window.requestAnimationFrame(this.makeFrame.bind(this)); } }; return AnimationPlayer; }(); function waiting(evaluation, timeoutSeconds) { return tslib.__awaiter(this, void 0, void 0, function () { var max; return tslib.__generator(this, function (_a) { max = (timeoutSeconds || 5) * 1000; return [2 /*return*/ , new Promise(function (resolve) { var start = new Date().getTime(); var inter = setInterval(function () { //타임아웃 체크 var time = new Date().getTime(); if (time - start > max) { clearInterval(inter); resolve(false); return; } //평가식 체크 if (evaluation()) { clearInterval(inter); resolve(true); } }, 100); })]; }); }); } var getClusterInfo = function (basePixelSize, mapBounds, mapWidth, mapHeight, itemList, sizeFunction) { var _a; //1. basePixelSize 기준으로 현재 지도 크기를 베이스로 영역 갯수 정하기 var rowCount = Number((mapWidth / basePixelSize).toFixed(0)) || 1; var colCount = Number((mapHeight / basePixelSize).toFixed(0)) || 1; //console.log('rowCount', rowCount, 'colCount', colCount) var boundsLineSizeX = Number(((mapBounds.ne.lng - mapBounds.nw.lng) / rowCount).toFixed(7)); var boundsLineSizeY = Number(((mapBounds.nw.lat - mapBounds.se.lat) / colCount).toFixed(7)); //console.log('boundsLineSize', boundsLineSizeX, boundsLineSizeY) var boundsPos = []; var tempX1, tempY1, tempX2, tempY2; for (var i = 0; i < rowCount; i++) { tempX1 = mapBounds.nw.lng + boundsLineSizeX * i; tempX2 = mapBounds.nw.lng + boundsLineSizeX * (i + 1); var rows = []; boundsPos.push(rows); for (var k = 0; k < colCount; k++) { tempY2 = mapBounds.se.lat + boundsLineSizeY * k; tempY1 = mapBounds.se.lat + boundsLineSizeY * (k + 1); var thisBounds = Bounds.fromNWSE(new Position(tempY1, tempX1), new Position(tempY2, tempX2)); var includedList = thisBounds.getIncludedPositions(itemList); rows.push({ bounds: thisBounds, checked: false, center: false, centerPosition: thisBounds.getCenter(), incList: [], itemList: includedList, size: basePixelSize }); } } //좌표마다 검사해서 인접셀 병합 처리 var centerList = []; var totalItemCount = 0; var min; var max; for (var i = 0; i < boundsPos.length; i++) { for (var k = 0; k < boundsPos[i].length; k++) { var curr = boundsPos[i][k]; if (curr.checked) continue; curr.checked = true; //현재기준 8방향 객체 모으기 var incList = []; if (boundsPos[i]) { boundsPos[i][k - 1] && incList.push(boundsPos[i][k - 1]); boundsPos[i][k + 1] && incList.push(boundsPos[i][k + 1]); } if (boundsPos[i - 1]) { boundsPos[i - 1][k - 1] && incList.push(boundsPos[i - 1][k - 1]); boundsPos[i - 1][k] && incList.push(boundsPos[i - 1][k]); boundsPos[i - 1][k + 1] && incList.push(boundsPos[i - 1][k + 1]); } if (boundsPos[i + 1]) { boundsPos[i + 1][k + 1] && incList.push(boundsPos[i + 1][k + 1]); boundsPos[i + 1][k] && incList.push(boundsPos[i + 1][k]); boundsPos[i + 1][k - 1] && incList.push(boundsPos[i + 1][k - 1]); } for (var _i = 0, incList_1 = incList; _i < incList_1.length; _i++) { var inc = incList_1[_i]; if (inc.checked) continue; inc.checked = true; if (inc.itemList && inc.itemList.length > 0) { curr.incList.push(inc); (_a = curr.itemList).push.apply(_a, inc.itemList); curr.center = true; } } if (curr.center) { centerList.push(curr); var avrLat = calculateAverage(curr.itemList.map(function (item) { return item.lat; })); var avrLng = calculateAverage(curr.itemList.map(function (item) { return item.lng; })); curr.centerPosition = new Position(avrLat, avrLng); totalItemCount += curr.itemList.length; if (!min || curr.itemList.length < min) { min = curr.itemList.length; } if (!max || curr.itemList.length > max) { max = curr.itemList.length; } } } } var status = { total: totalItemCount, average: totalItemCount / centerList.length, min: min, max: max }; sizeFunction = sizeFunction || function (info, status) { var minSize = basePixelSize / 4; var maxSize = basePixelSize; return Math.min(Math.max(basePixelSize * info.itemList.length / status.average, minSize), maxSize); }; for (var _b = 0, centerList_1 = centerList; _b < centerList_1.length; _b++) { var center = centerList_1[_b]; center.size = sizeFunction(center, status); } // console.log('centerList', centerList, status); return centerList; }; var calculateAverage = function (nums) { var sum = 0; for (var _i = 0, nums_1 = nums; _i < nums_1.length; _i++) { var num = nums_1[_i]; sum += num; } return Number((sum / nums.length).toFixed(7)); }; function log(debug, label) { var args = []; for (var _i = 2; _i < arguments.length; _i++) { args[_i - 2] = arguments[_i]; } if (!debug) return; args && console.log.apply(console, tslib.__spreadArray(['[mint-map debug]', label || ''], args, false)); } var MintMapStatus = /** @class */ function () { function MintMapStatus() { this.marker = 0; this.byLabel = new Map(); } MintMapStatus.prototype.init = function () { this.marker = 0; this.byLabel.clear(); }; MintMapStatus.prototype.print = function () { var str = "[mint-map status]\n\nmarker : ".concat(this.marker, "\n "); if (this.byLabel.size > 0) { str += '\n-------- status detail (by label) ----------'; this.byLabel.forEach(function (val, key) { str += "\n(".concat(key, ") : ").concat(val); }); str += '\n\n'; } console.log(str); }; MintMapStatus.prototype.setMarker = function (inc, label) { this.marker += inc; if (label) { var curr = this.byLabel.get(label); var calc = 0; if (curr === undefined) { calc = inc; } else { calc = curr + inc; } if (calc === 0) { this.byLabel.delete(label); } else { this.byLabel.set(label, calc); } } }; return MintMapStatus; }(); var Status = new MintMapStatus(); function getMapOfType(mapType, map) { if (!map) { return undefined; } var base = window[mapType]; return base && map instanceof base.maps.Map ? map : undefined; } var MintMapController = /** @class */ function () { //constructor function MintMapController(props) { this.mapApiLoaded = false; this.mapInitialized = false; this.processedTime = 0; //props this.mapProps = props; } //기본 기능 MintMapController.prototype.getMap = function () { return this.map; }; MintMapController.prototype.getMapType = function () { return this.type; }; //기본 기능 - 좌표 변환 MintMapController.prototype.positionToOffset = function (position) { var div = this.mapDivElement; var w = div === null || div === void 0 ? void 0 : div.offsetWidth; var h = div === null || div === void 0 ? void 0 : div.offsetHeight; var bounds = this.getCurrBounds(); var maxLng = bounds.ne.lng; var minLng = bounds.sw.lng; var lng = Math.abs(maxLng - minLng); var x = w * (position.lng - minLng) / lng; var maxLat = bounds.ne.lat; var minLat = bounds.sw.lat; var lat = Math.abs(maxLat - minLat); var y = h * (maxLat - position.lat) / lat; return new Offset(x, y); }; MintMapController.prototype.offsetToPosition = function (offset) { var div = this.mapDivElement; var w = div === null || div === void 0 ? void 0 : div.offsetWidth; var h = div === null || div === void 0 ? void 0 : div.offsetHeight; var bounds = this.getCurrBounds(); var maxLng = bounds.ne.lng; var minLng = bounds.sw.lng; var lng = Math.abs(maxLng - minLng); //const x = w * (position.lng - minLng) / lng var lngVal = offset.x * lng / w + minLng; var maxLat = bounds.ne.lat; var minLat = bounds.sw.lat; var lat = Math.abs(maxLat - minLat); //const y = h * (maxLat - position.lat) / lat var latVal = (offset.y * lat / h - maxLat) * -1; return new Position(latVal, lngVal); }; //스크립트 로드 MintMapController.prototype.loadScript = function (url, id, checkLoaded) { return tslib.__awaiter(this, void 0, void 0, function () { return tslib.__generator(this, function (_a) { //boolean -> script 로드 후 callback 실행 여부 //** callback 실행이 여기서 안된 경우는 바깥에서 해줘야한다. return [2 /*return*/ , new Promise(function (resolve) { //이미 로드되어 있으면 종료 if (checkLoaded()) { resolve(false); return; } var prevElement = id ? document.getElementById(id) : undefined; //기존 스크립트 로드 정보가 있으면 if (prevElement) { // console.log(`already loaded script => ${id}`) //로드 되어 있지 않으면 스크립트 load 이벤트 끼워넣기 prevElement.addEventListener('load', function () { // console.log('script loaded!!!') resolve(false); }); return; } //스크립트 로드 처리 var script = document.createElement('script'); script.src = url; script.async = true; script.defer = true; id && (script.id = id); script.addEventListener('load', function () { resolve(true); }); document.body.appendChild(script); })]; }); }); }; MintMapController.prototype.getRandomFunctionName = function (prefix) { return "".concat(prefix, "_").concat(uuid.v4().replace(/-/g, '_')); }; /** * URL 빌더 메서드 * * @param {string} baseUrl: 기본 URL * @param {{ [ key: string ]: string | string[] }} param: 파라미터 JSON * @returns {string} URL */ MintMapController.prototype.buildUrl = function (baseUrl, param) { var params = Object.entries(param).map(function (_a) { var key = _a[0], value = _a[1]; var temp = Array.isArray(value) ? value.join(',') : value; return "".concat(key, "=").concat(temp); }).join('&'); return "".concat(baseUrl, "?").concat(params); }; /** * 쓰로틀링 처리 * @returns */ MintMapController.prototype.checkBoundsChangeThrottleTime = function () { if (!this.mapProps.boundsChangeThrottlingTime) return true; //boundsChangeThrottlingTime 이내의 연속 요청은 제외시킴 var time = new Date().getTime(); if (this.processedTime > 0 && time - this.processedTime < this.mapProps.boundsChangeThrottlingTime) { return false; } else { this.processedTime = time; return true; } }; MintMapController.prototype.getBaseToMapZoom = function (zoomBase) { var baseMap = MapZoomInfo.BASE_TO_MAP.get(zoomBase); if (baseMap) { var mapZoomInfo = baseMap.get(this.getMapType()); if (mapZoomInfo) { return mapZoomInfo.level; } } throw new Error("[getBaseToMapZoom][".concat(zoomBase, "] is not valid zoom level")); }; MintMapController.prototype.getMapToBaseZoom = function (mapZoom) { var baseZoom = MapZoomInfo.MAP_TO_BASE.get(this.getMapType() + mapZoom); if (baseZoom) { return baseZoom; } throw new Error("[getMapToBaseZoom][".concat(mapZoom, "] is not valid zoom level")); }; MintMapController.prototype.morph = function (position, zoom, option) { var naverMap = getMapOfType('naver', this.map); if (naverMap) { naverMap.morph(position, zoom, tslib.__assign({}, option)); } else { // 센터로 이동 this.setCenter(position); // 줌 이동 this.setZoomLevel(zoom); } }; MintMapController.prototype.printStatus = function () { Status.print(); }; return MintMapController; }(); var MapZoomInfo = /** @class */ function () { function MapZoomInfo() {} MapZoomInfo.BASE_TO_MAP = new Map([[1, new Map([['google', { level: 1 }], ['naver', { level: 6 }], ['kakao', { level: 14 }]])], [2, new Map([['google', { level: 2, distance: 2000, unit: 'km' }], ['naver', { level: 6 }], ['kakao', { level: 14 }]])], [3, new Map([['google', { level: 3, distance: 1000, unit: 'km' }], ['naver', { level: 6 }], ['kakao', { level: 14 }]])], [4, new Map([['google', { level: 4, distance: 500, unit: 'km' }], ['naver', { level: 6 }], ['kakao', { level: 14 }]])], [5, new Map([['google', { level: 5, distance: 200, unit: 'km' }], ['naver', { level: 6 }], ['kakao', { level: 14 }]])], [6, new Map([['google', { level: 6, distance: 100, unit: 'km' }], ['naver', { level: 6 }], ['kakao', { level: 14 }]])], [7, new Map([['google', { level: 7, distance: 50, unit: 'km' }], ['naver', { level: 7 }], ['kakao', { level: 13 }]])], [8, new Map([['google', { level: 8, distance: 20, unit: 'km' }], ['naver', { level: 8 }], ['kakao', { level: 12 }]])], [9, new Map([['google', { level: 9, distance: 10, unit: 'km' }], ['naver', { level: 9 }], ['kakao', { level: 11 }]])], [10, new Map([['google', { level: 10, distance: 5, unit: 'km' }], ['naver', { level: 10 }], ['kakao', { level: 10 }]])], [11, new Map([['google', { level: 11, distance: 2, unit: 'km' }], ['naver', { level: 11 }], ['kakao', { level: 9 }]])], [12, new Map([['google', { level: 12, distance: 1, unit: 'km' }], ['naver', { level: 12 }], ['kakao', { level: 8 }]])], [13, new Map([['google', { level: 13, distance: 500, unit: 'm' }], ['naver', { level: 13 }], ['kakao', { level: 7 }]])], [14, new Map([['google', { level: 14, distance: 500, unit: 'm' }], ['naver', { level: 14 }], ['kakao', { level: 6 }]])], [15, new Map([['google', { level: 15, distance: 500, unit: 'm' }], ['naver', { level: 15 }], ['kakao', { level: 5 }]])], [16, new Map([['google', { level: 16, distance: 500, unit: 'm' }], ['naver', { level: 16 }], ['kakao', { level: 4 }]])], [17, new Map([['google', { level: 17, distance: 500, unit: 'm' }], ['naver', { level: 17 }], ['kakao', { level: 3 }]])], [18, new Map([['google', { level: 18, distance: 500, unit: 'm' }], ['naver', { level: 18 }], ['kakao', { level: 2 }]])], [19, new Map([['google', { level: 19, distance: 500, unit: 'm' }], ['naver', { level: 19 }], ['kakao', { level: 1 }]])], [20, new Map([['google', { level: 20, distance: 500, unit: 'm' }], ['naver', { level: 20 }], ['kakao', { level: 1 }]])], [21, new Map([['google', { level: 21, distance: 500, unit: 'm' }], ['naver', { level: 21 }], ['kakao', { level: 1 }]])], [22, new Map([['google', { level: 22, distance: 500, unit: 'm' }], ['naver', { level: 21 }], ['kakao', { level: 1 }]])]]); MapZoomInfo.MAP_TO_BASE = new Map(Array.from(MapZoomInfo.BASE_TO_MAP.entries()).map(function (item) { var base = item[0]; var mapZoom = item[1]; var result = []; mapZoom.forEach(function (value, key) { result.push([key + value.level, base]); }); return result; }).flatMap(function (a) { return a; })); return MapZoomInfo; }(); // export type MapEvent = 'bounds_changed'|'center_changed'|'idle'|'zoom_changed'|'zoomstart' // export type MapUIEvent = 'click'|'dblclick'|'' var MapEvent = /** @class */ function () { function MapEvent() { this.BOUNDS_CHANGED = 'bounds_changed'; this.CENTER_CHANGED = 'center_changed'; this.IDLE = 'idle'; this.ZOOM_CHANGED = 'zoom_changed'; this.ZOOMSTART = 'zoomstart'; } MapEvent.prototype.get = function (eventName) { var value = this[eventName]; if (typeof value === 'string') { return value; } }; return MapEvent; }(); var MapUIEvent = /** @class */ function () { function MapUIEvent() { this.CLICK = 'click'; this.DBLCLICK = 'dblclick'; this.MOUSEDOWN = 'mousedown'; this.MOUSEUP = 'mouseup'; this.MOUSEOUT = 'mouseout'; this.MOUSEMOVE = 'mousemove'; this.MOUSEOVER = 'mouseover'; this.DRAG = 'drag'; this.DRAGSTART = 'dragstart'; this.DRAGEND = 'dragend'; this.RIGHTCLICK = 'rightclick'; this.CONTEXTMENU = 'contextmenu'; } MapUIEvent.prototype.get = function (eventName) { var value = this[eventName]; if (typeof value === 'string') { return value; } }; return MapUIEvent; }(); var MintMapCanvasRenderer = /** @class */ function () { function MintMapCanvasRenderer(context) { this.context = context; } return MintMapCanvasRenderer; }(); function SVGCircle(_a) { var _b = _a.radius, radius = _b === void 0 ? 100 : _b, _c = _a.background, background = _c === void 0 ? 'lightgreen' : _c, children = _a.children, _d = _a.svgProperties, svgProperties = _d === void 0 ? {} : _d, _e = _a.shapeProperties, shapeProperties = _e === void 0 ? {} : _e; var _f = React.useState(radius * 2), boxSize = _f[0], setBoxSize = _f[1]; React.useEffect(function () { // console.log('SVGCircle radius', radius); setBoxSize(radius * 2); }, [radius]); return React__default["default"].createElement(React__default["default"].Fragment, null, React__default["default"].createElement("svg", tslib.__assign({ pointerEvents: "none", width: boxSize, height: boxSize, viewBox: "0 0 ".concat(boxSize, " ").concat(boxSize), overflow: 'visible' }, svgProperties), React__default["default"].createElement("circle", tslib.__assign({ pointerEvents: "visiblepainted", cx: radius, cy: radius, r: radius, fill: background }, shapeProperties))), React__default["default"].createElement("div", { style: { pointerEvents: 'none', position: 'absolute', left: '0px', top: '0px', width: "".concat(boxSize, "px"), height: "".concat(boxSize, "px") } }, children)); } var Drawable = /** @class */ function () { function Drawable() {} return Drawable; }(); var Marker = /** @class */ function (_super) { tslib.__extends(Marker, _super); /** * 지도에 표시할 마커정보 */ function Marker(options) { var _this = _super.call(this) || this; _this.options = options; return _this; } return Marker; }(Drawable); var Polyline = /** @class */ function (_super) { tslib.__extends(Polyline, _super); /** * 지도에 표시할 폴리곤정보 */ function Polyline(options) { var _this = _super.call(this) || this; _this.options = options; return _this; } return Polyline; }(Drawable); var Polygon = /** @class */ function (_super) { tslib.__extends(Polygon, _super); /** * 지도에 표시할 폴리곤정보 */ function Polygon(options) { var _this = _super.call(this) || this; _this.options = options; return _this; } /** * 폴리곤의 중점을 구한다. */ Polygon.prototype.getCenter = function () { if (Array.isArray(this.options.position) && this.options.position.length > 0) { var paths = this.options.position.map(function (elem) { return elem instanceof Position ? elem : new Position(elem[0], elem[1]); }); return PolygonCalculator.getCenter(paths); } throw new Error('center 를 찾을 수 없습니다.'); }; return Polygon; }(Drawable); function SVGPolygon(_a) { var path = _a.path, _b = _a.innerPath, innerPath = _b === void 0 ? [] : _b, _c = _a.background, background = _c === void 0 ? 'lightblue' : _c, _d = _a.svgProperties, svgProperties = _d === void 0 ? {} : _d, _e = _a.shapeProperties, shapeProperties = _e === void 0 ? {} : _e, _f = _a.mode, mode = _f === void 0 ? 'POLYGON' : _f, children = _a.children; var getPolygonInfo = React.useCallback(function (path) { var maxX, minX, maxY, minY; for (var _i = 0, path_1 = path; _i < path_1.length; _i++) { var offset = path_1[_i]; if (maxX === undefined || offset.x > maxX) { maxX = offset.x; } if (minX === undefined || offset.x < minX) { minX = offset.x; } if (maxY === undefined || offset.y > maxY) { maxY = offset.y; } if (minY === undefined || offset.y < minY) { minY = offset.y; } } if (!maxX || !minX || !maxY || !minY) { return { containerLeft: 0, containerTop: 0, containerWidth: 0, containerHeight: 0 }; } var width = maxX - minX; var height = maxY - minY; return { containerLeft: minX, containerTop: minY, containerWidth: width, containerHeight: height }; }, []); var getD = React.useCallback(function (path, innerPath, mode) { var isPolygon = mode === 'POLYGON'; var out = ''; //path out += getLine(path, isPolygon); //inner path for (var _i = 0, innerPath_1 = innerPath; _i < innerPath_1.length; _i++) { var inner = innerPath_1[_i]; out += ' ' + getLine(inner, isPolygon); } return out; }, []); var getLine = React.useCallback(function (path, isPolygon) { return path.map(function (offset, idx) { if (idx === 0) { return "M ".concat(offset.x, ",").concat(offset.y); } else if (idx === 1) { return "L ".concat(offset.x, ",").concat(offset.y); } else { if (offset.equals(path[idx - 1])) { return ''; } else { return "".concat(offset.x, ",").concat(offset.y); } } }).join(' ')