renusify 2.5.2 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/app/index.vue +74 -22
- package/components/app/toast/index.vue +76 -71
- package/components/app/toast/toast.vue +62 -44
- package/components/avatar/index.vue +208 -84
- package/components/button/buttonConfirm.vue +53 -26
- package/components/button/buttonGroup.js +0 -2
- package/components/button/buttonGroup.vue +310 -62
- package/components/button/index.vue +584 -100
- package/components/calendar/index.js +0 -2
- package/components/calendar/index.vue +326 -262
- package/components/calendar/month.vue +64 -55
- package/components/calendar/year.vue +30 -25
- package/components/card/index.vue +139 -59
- package/components/codeEditor/highlightCss.vue +38 -39
- package/components/codeEditor/highlightHtml.vue +64 -64
- package/components/codeEditor/highlightJs.vue +37 -38
- package/components/codeEditor/index.vue +129 -79
- package/components/codeEditor/run.vue +225 -39
- package/components/codeEditor/useCodeFormatter.js +150 -0
- package/components/confirm/index.vue +139 -80
- package/components/container/col.vue +5 -4
- package/components/container/divider.vue +28 -19
- package/components/container/index.vue +34 -15
- package/components/container/row.vue +26 -9
- package/components/container/spacer.vue +2 -4
- package/components/container/style.scss +3 -0
- package/components/content/index.vue +49 -32
- package/components/cropper/index.vue +401 -244
- package/components/float/index.vue +542 -415
- package/components/form/addressInput/index.vue +184 -109
- package/components/form/camInput/index.vue +370 -244
- package/components/form/checkInput/index.vue +138 -71
- package/components/form/checkboxInput/index.vue +87 -47
- package/components/form/colorInput/Alpha.vue +81 -83
- package/components/form/colorInput/Hue.vue +91 -68
- package/components/form/colorInput/Preview.vue +43 -47
- package/components/form/colorInput/Saturation.vue +101 -86
- package/components/form/colorInput/index.vue +71 -39
- package/components/form/colorInput/picker.vue +111 -106
- package/components/form/colorInput/useColor.js +153 -0
- package/components/form/dateInput/index.vue +691 -356
- package/components/form/dateInput/month.vue +63 -54
- package/components/form/dateInput/year.vue +35 -25
- package/components/form/fileInput/index.js +0 -1
- package/components/form/fileInput/index.vue +263 -106
- package/components/form/fileInput/single.vue +323 -164
- package/components/form/groupInput/index.vue +199 -101
- package/components/form/index.vue +189 -83
- package/components/form/input/index.vue +416 -377
- package/components/form/jsonInput/JsonView.vue +54 -56
- package/components/form/jsonInput/index.vue +247 -165
- package/components/form/maskInput/index.vue +252 -132
- package/components/form/numberInput/index.js +0 -1
- package/components/form/numberInput/index.vue +226 -117
- package/components/form/passwordInput/index.js +2 -1
- package/components/form/passwordInput/index.vue +269 -102
- package/components/form/radioInput/index.vue +143 -72
- package/components/form/rangeInput/index.vue +280 -167
- package/components/form/ratingInput/index.vue +57 -57
- package/components/form/selectInput/index.js +1 -3
- package/components/form/selectInput/index.vue +584 -296
- package/components/form/switchInput/index.vue +73 -59
- package/components/form/telInput/index.js +0 -1
- package/components/form/telInput/index.vue +238 -135
- package/components/form/textArea/index.vue +72 -35
- package/components/form/textEditor/index.vue +739 -0
- package/components/form/{text-editor → textEditor}/style.scss +8 -16
- package/components/form/textInput/index.vue +54 -32
- package/components/form/timeInput/index.vue +82 -55
- package/components/form/timeInput/range.vue +115 -94
- package/components/form/timeInput/timepicker.vue +382 -449
- package/components/form/uniqueInput/index.vue +105 -48
- package/components/form/unitInput/index.vue +139 -84
- package/components/formCreator/index.js +0 -1
- package/components/formCreator/index.vue +314 -148
- package/components/highlight/index.vue +41 -25
- package/components/highlight/style.scss +2 -2
- package/components/highlight/{mixin.js → useHighlight.js} +181 -160
- package/components/icon/index.vue +79 -33
- package/components/img/index.vue +249 -147
- package/components/img/preview.vue +180 -198
- package/components/img/svgImg.vue +42 -39
- package/components/index.js +5 -20
- package/components/infinite/index.js +1 -2
- package/components/infinite/index.vue +248 -66
- package/components/map/index.vue +428 -261
- package/components/map/route.vue +794 -487
- package/components/map/select.vue +118 -58
- package/components/menu/index.vue +201 -91
- package/components/meta/meta.js +26 -3
- package/components/modal/index.vue +382 -156
- package/components/notify/index.vue +204 -86
- package/components/notify/notification.vue +38 -55
- package/components/progress/circle.vue +189 -70
- package/components/progress/line.vue +266 -46
- package/components/searchBox/index.js +1 -3
- package/components/searchBox/index.vue +194 -101
- package/components/skeleton/index.vue +45 -20
- package/components/slider/index.vue +318 -156
- package/components/swiper/index.vue +254 -106
- package/components/table/crud/footer.vue +77 -53
- package/components/table/crud/header.vue +71 -72
- package/components/table/crud/index.vue +629 -399
- package/components/table/index.vue +721 -278
- package/components/timeAgo/index.vue +145 -96
- package/components/tour/index.vue +338 -235
- package/components/tree/index.vue +235 -89
- package/components/tree/tree-element.vue +106 -106
- package/directive/animate/index.js +77 -0
- package/directive/clickOutSide/index.js +98 -0
- package/directive/drag/index.js +153 -0
- package/directive/index.js +11 -13
- package/directive/intersect/index.js +263 -0
- package/directive/mask/index.js +67 -0
- package/directive/parallax/index.js +78 -0
- package/directive/ripple/index.js +14 -0
- package/directive/scroll/index.js +244 -0
- package/directive/sortable/index.js +274 -0
- package/directive/title/index.js +75 -0
- package/directive/touch/index.js +268 -0
- package/index.js +10 -8
- package/package.json +5 -2
- package/plugins/validation/Validate.js +88 -79
- package/scripts/generate-docs.mjs +226 -0
- package/scripts/menu.mjs +240 -0
- package/scripts/parser.mjs +1086 -0
- package/style/_index.scss +7 -0
- package/style/app.scss +13 -65
- package/style/colors.scss +5 -22
- package/style/functions/index.scss +8 -0
- package/style/mixins/index.scss +17 -5
- package/style/variables/base.scss +154 -175
- package/style/variables/color.scss +0 -12
- package/style/variables/utilities.scss +0 -180
- package/tools/helper.js +0 -8
- package/tools/icons.js +6 -1
- package/tools/root.js +71 -0
- package/components/app/style.scss +0 -41
- package/components/app/toast/style.scss +0 -20
- package/components/avatar/style.scss +0 -32
- package/components/bar/bottomNav.js +0 -1
- package/components/bar/bottomNav.vue +0 -28
- package/components/bar/bottomNavigationCircle.js +0 -2
- package/components/bar/bottomNavigationCircle.vue +0 -99
- package/components/bar/scss/bottomNav.scss +0 -67
- package/components/bar/scss/toolbar.scss +0 -174
- package/components/bar/toolbar/index.js +0 -8
- package/components/bar/toolbar/index.vue +0 -35
- package/components/bar/toolbar/laptop.vue +0 -33
- package/components/bar/toolbar/menuChilds.vue +0 -41
- package/components/bar/toolbar/menuLaptop.vue +0 -41
- package/components/bar/toolbar/menuMob.vue +0 -39
- package/components/bar/toolbar/mixin.js +0 -43
- package/components/bar/toolbar/mobile.vue +0 -34
- package/components/breadcrumb/bredcrumbItem.vue +0 -39
- package/components/breadcrumb/index.js +0 -3
- package/components/breadcrumb/index.vue +0 -71
- package/components/breadcrumb/style.scss +0 -51
- package/components/button/style.scss +0 -411
- package/components/card/style.scss +0 -86
- package/components/chart/chart.js +0 -1
- package/components/chart/chart.vue +0 -69
- package/components/chart/worldMap.js +0 -2
- package/components/chart/worldMap.vue +0 -1112
- package/components/chat/MessageList.vue +0 -163
- package/components/chat/chatInput.vue +0 -150
- package/components/chat/chatMsg.vue +0 -276
- package/components/chat/index.js +0 -11
- package/components/chat/index.vue +0 -113
- package/components/chip/index.js +0 -3
- package/components/chip/index.vue +0 -77
- package/components/chip/style.scss +0 -199
- package/components/codeEditor/mixin.js +0 -145
- package/components/countdown/index.js +0 -1
- package/components/countdown/index.vue +0 -105
- package/components/form/colorInput/mixin.js +0 -132
- package/components/form/fileInput/file.js +0 -148
- package/components/form/telInput/assets/flags.png +0 -0
- package/components/form/telInput/assets/flags@2x.png +0 -0
- package/components/form/text-editor/index.vue +0 -705
- package/components/icon/style.scss +0 -17
- package/components/infinite/div.js +0 -6
- package/components/infinite/div.vue +0 -193
- package/components/infinite/page.js +0 -3
- package/components/infinite/page.vue +0 -105
- package/components/list/index.js +0 -3
- package/components/list/index.vue +0 -122
- package/components/list/style.scss +0 -66
- package/components/message/index.js +0 -4
- package/components/message/index.vue +0 -40
- package/components/modal/style.scss +0 -146
- package/components/nestable/NestableItem.vue +0 -307
- package/components/nestable/editable.js +0 -44
- package/components/nestable/index.js +0 -1
- package/components/nestable/index.vue +0 -226
- package/components/nestable/methods.js +0 -416
- package/components/progress/style.scss +0 -229
- package/components/table/style.scss +0 -338
- package/components/tabs/index.js +0 -3
- package/components/tabs/index.vue +0 -151
- package/components/timeline/index.js +0 -6
- package/components/timeline/index.vue +0 -76
- package/directive/resize/index.js +0 -30
- package/directive/skeleton/index.js +0 -27
- package/directive/skeleton/style.scss +0 -37
- package/plugins/request/Request.js +0 -68
- package/style/animation.scss +0 -94
- package/style/style.scss +0 -8
- package/tools/rootable.js +0 -75
- /package/components/form/{text-editor → textEditor}/index.js +0 -0
- /package/components/form/{text-editor → textEditor}/preview.js +0 -0
- /package/components/form/{text-editor → textEditor}/preview.vue +0 -0
package/components/map/route.vue
CHANGED
|
@@ -7,12 +7,26 @@
|
|
|
7
7
|
</r-col>
|
|
8
8
|
<r-col :style="'height: '+height" class="overflow-y-auto col-12 sm-3">
|
|
9
9
|
<div class="pa-2">
|
|
10
|
+
<!--
|
|
11
|
+
@slot Header slot for custom route summary display
|
|
12
|
+
@binding {String} distance - Formatted distance string (e.g., "15.25 km")
|
|
13
|
+
@binding {String} time - Human-readable time duration (e.g., "2 hours 15 minutes")
|
|
14
|
+
-->
|
|
10
15
|
<slot :distance="distance" :time="time" name="header">
|
|
11
16
|
<div class="bodey-1">{{ $helper.ifHas(modelValue, '------', 'summary', 'name') }}</div>
|
|
12
17
|
<r-divider class="my-3"></r-divider>
|
|
13
18
|
<div>{{ time || '---' }}</div>
|
|
14
19
|
<div>{{ distance || '0 km' }}</div>
|
|
15
20
|
</slot>
|
|
21
|
+
|
|
22
|
+
<!--
|
|
23
|
+
@slot Points slot for custom waypoints list display
|
|
24
|
+
@binding {Function} add - Function to add a new waypoint
|
|
25
|
+
@binding {Function} del - Function to delete a waypoint
|
|
26
|
+
@binding {Function} open - Function to open a waypoint on the map
|
|
27
|
+
@binding {Object} points - Object containing all waypoints
|
|
28
|
+
@binding {Function} to - Function to reorder waypoints
|
|
29
|
+
-->
|
|
16
30
|
<slot :add="add" :del="del" :open="open" :points="points" :to="to" name="points">
|
|
17
31
|
<transition-group name="slide-up">
|
|
18
32
|
<div v-for="(point,i) in points"
|
|
@@ -45,525 +59,818 @@
|
|
|
45
59
|
<span>{{ $t('map_new_point', 'renusify') }}</span>
|
|
46
60
|
</r-btn>
|
|
47
61
|
</slot>
|
|
62
|
+
|
|
63
|
+
<!--
|
|
64
|
+
@slot Footer slot for additional content below the points list
|
|
65
|
+
-->
|
|
48
66
|
<slot name="footer"></slot>
|
|
49
67
|
</div>
|
|
50
68
|
</r-col>
|
|
51
69
|
</r-row>
|
|
52
70
|
</r-container>
|
|
53
71
|
</template>
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
|
|
73
|
+
<script setup>
|
|
74
|
+
import {ref, computed, onMounted, inject} from 'vue'
|
|
75
|
+
|
|
76
|
+
const props = defineProps({
|
|
77
|
+
/**
|
|
78
|
+
* Route data model value
|
|
79
|
+
* @type {Object}
|
|
80
|
+
* @model
|
|
81
|
+
* @description Contains route data including points, summary, and waypoints
|
|
82
|
+
* @property {Object} points - Waypoint coordinates keyed by unique IDs
|
|
83
|
+
* @property {Object} summary - Route summary with name, time, and distance
|
|
84
|
+
* @property {Array} waypoints - Detailed waypoint information
|
|
85
|
+
* @example
|
|
86
|
+
* {
|
|
87
|
+
* points: { 'abc123': [35.6997, 51.3380], 'def456': [35.7000, 51.3400] },
|
|
88
|
+
* summary: { name: 'Tehran Route', time: 3600, distance: 5000 },
|
|
89
|
+
* waypoints: [{ name: 'Start Point' }, { name: 'End Point' }]
|
|
90
|
+
* }
|
|
91
|
+
*/
|
|
92
|
+
modelValue: Object,
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Transportation mode for route calculation
|
|
96
|
+
* @type {String}
|
|
97
|
+
* @default 'car'
|
|
98
|
+
* @validator ['car', 'foot', 'bike']
|
|
99
|
+
* @description Determines which OSRM profile to use for routing
|
|
100
|
+
* - 'car': Optimized for car travel (fastest route)
|
|
101
|
+
* - 'foot': Optimized for walking (pedestrian paths)
|
|
102
|
+
* - 'bike': Optimized for cycling (bike lanes, lower gradients)
|
|
103
|
+
*/
|
|
104
|
+
by: {
|
|
105
|
+
type: String,
|
|
106
|
+
default: 'car',
|
|
107
|
+
validator: (value) => ['car', 'foot', 'bike'].includes(value)
|
|
74
108
|
},
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Map container height
|
|
112
|
+
* @type {String}
|
|
113
|
+
* @default '500px'
|
|
114
|
+
* @description CSS height for the map and sidebar container
|
|
115
|
+
* @example '400px'
|
|
116
|
+
* @example '80vh'
|
|
117
|
+
* @example '100%'
|
|
118
|
+
*/
|
|
119
|
+
height: {type: String, default: "500px"},
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Time margin multiplier for route duration
|
|
123
|
+
* @type {Number}
|
|
124
|
+
* @default 1
|
|
125
|
+
* @description Multiplier applied to calculated route time for buffer/planning
|
|
126
|
+
* @example 1.2 // Adds 20% buffer to estimated time
|
|
127
|
+
* @example 1.5 // Adds 50% buffer to estimated time
|
|
128
|
+
*/
|
|
129
|
+
marginTime: {type: Number, default: 1},
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Route line colors
|
|
133
|
+
* @type {Array<String>}
|
|
134
|
+
* @default []
|
|
135
|
+
* @description Array of CSS color values for route segments between waypoints
|
|
136
|
+
* If array length < segments, colors cycle through available values
|
|
137
|
+
* @example ['#FF0000', '#00FF00'] // Red first segment, green second segment
|
|
138
|
+
* @example ['blue'] // All segments blue
|
|
139
|
+
*/
|
|
140
|
+
color: {
|
|
141
|
+
type: Array,
|
|
142
|
+
default: () => []
|
|
89
143
|
},
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Maximum number of waypoints allowed
|
|
147
|
+
* @type {Number}
|
|
148
|
+
* @default 3
|
|
149
|
+
* @description Limits the number of waypoints that can be added to the route
|
|
150
|
+
* @example 5 // Allows up to 5 waypoints
|
|
151
|
+
* @example 10 // Allows up to 10 waypoints
|
|
152
|
+
*/
|
|
153
|
+
maxPoints: {type: Number, default: 3},
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Enable dark mode for the map
|
|
157
|
+
* @type {Boolean}
|
|
158
|
+
* @default false
|
|
159
|
+
* @description Applies dark theme to the map display
|
|
160
|
+
*/
|
|
161
|
+
darkMode: Boolean,
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Enable waypoint editing features
|
|
165
|
+
* @type {Boolean}
|
|
166
|
+
* @default false
|
|
167
|
+
* @description When true, allows adding, deleting, dragging, and reordering waypoints
|
|
168
|
+
*/
|
|
169
|
+
editable: Boolean
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
const emit = defineEmits([
|
|
173
|
+
/**
|
|
174
|
+
* Updated route data object
|
|
175
|
+
*/
|
|
176
|
+
'update:modelValue'
|
|
177
|
+
])
|
|
178
|
+
|
|
179
|
+
// Inject renusify dependencies
|
|
180
|
+
const {$r, $helper, $t} = inject('renusify')
|
|
181
|
+
|
|
182
|
+
// Reactive state
|
|
183
|
+
const loading = ref(false)
|
|
184
|
+
const timeout_id = ref(null)
|
|
185
|
+
const req_id = ref(null)
|
|
186
|
+
const map = ref(null)
|
|
187
|
+
const L = ref(null)
|
|
188
|
+
const center = ref([])
|
|
189
|
+
const markers = ref(null)
|
|
190
|
+
const lines = ref(null)
|
|
191
|
+
const routing = ref({})
|
|
192
|
+
const points = ref($helper.ifHas(props.modelValue, {}, 'points'))
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Computed array of waypoint IDs
|
|
196
|
+
* @type {ComputedRef<Array<String>>}
|
|
197
|
+
*/
|
|
198
|
+
const points_keys = computed(() => Object.keys(points.value))
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Computed array of waypoint coordinates
|
|
202
|
+
* @type {ComputedRef<Array<Array<Number>>>}
|
|
203
|
+
*/
|
|
204
|
+
const points_vals = computed(() => Object.values(points.value))
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Computed human-readable route duration
|
|
208
|
+
* @type {ComputedRef<String>}
|
|
209
|
+
*/
|
|
210
|
+
const time = computed(() => {
|
|
211
|
+
return timeHumanity($helper.ifHas(props.modelValue, 0, 'summary', 'time'))
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Computed formatted route distance
|
|
216
|
+
* @type {ComputedRef<String>}
|
|
217
|
+
*/
|
|
218
|
+
const distance = computed(() => {
|
|
219
|
+
if (!$helper.ifHas(props.modelValue, false, 'summary', 'distance')) {
|
|
220
|
+
return ''
|
|
221
|
+
}
|
|
222
|
+
return ((props.modelValue.summary.distance / 1000).toFixed(2)) + ' km'
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Computed origin marker icon
|
|
227
|
+
* @type {ComputedRef<String>}
|
|
228
|
+
*/
|
|
229
|
+
const iconOrigin = computed(() => require('./images/origin.png'))
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Computed intermediate point marker icon
|
|
233
|
+
* @type {ComputedRef<String>}
|
|
234
|
+
*/
|
|
235
|
+
const iconPoint = computed(() => require('./images/point.png'))
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Computed destination marker icon
|
|
239
|
+
* @type {ComputedRef<String>}
|
|
240
|
+
*/
|
|
241
|
+
const iconDestination = computed(() => require('./images/destination.png'))
|
|
242
|
+
|
|
243
|
+
// Initialize component
|
|
244
|
+
onMounted(() => {
|
|
245
|
+
get()
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
// Methods
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Get display position label for a waypoint
|
|
252
|
+
* @method pos
|
|
253
|
+
* @param {Number} i - Waypoint index
|
|
254
|
+
* @returns {String} Position label (e.g., "Origin", "Point 2", "Destination")
|
|
255
|
+
*/
|
|
256
|
+
const pos = (i) => {
|
|
257
|
+
let n = i + 1
|
|
258
|
+
if (i === 0) {
|
|
259
|
+
n = $t('map_origin', 'renusify')
|
|
260
|
+
} else if (i === points_vals.value.length - 1) {
|
|
261
|
+
n = $t('map_destination', 'renusify')
|
|
262
|
+
}
|
|
263
|
+
return $t(['map_point', [n]], 'renusify')
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Get full name/label for a waypoint
|
|
268
|
+
* @method name
|
|
269
|
+
* @param {Number} i - Waypoint index
|
|
270
|
+
* @returns {String} Complete waypoint name with position
|
|
271
|
+
*/
|
|
272
|
+
const name = (i) => {
|
|
273
|
+
return pos(i) + ': ' + $helper.ifHas(props.modelValue, '', 'waypoints', i, 'name')
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Open a waypoint's marker popup and center map on it
|
|
278
|
+
* @method open
|
|
279
|
+
* @param {Number} i - Waypoint index to open
|
|
280
|
+
*/
|
|
281
|
+
const open = (i) => {
|
|
282
|
+
if (markers.value && markers.value._layers) {
|
|
283
|
+
const markerLayers = Object.values(markers.value._layers)
|
|
284
|
+
if (markerLayers[i]) {
|
|
285
|
+
center.value = Object.values(markerLayers[i]._latlng)
|
|
286
|
+
markerLayers[i].openPopup()
|
|
94
287
|
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
if (!this.$helper.ifHas(this.modelValue, false, 'summary', 'distance')) {
|
|
108
|
-
return ''
|
|
109
|
-
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Delete a waypoint from the route
|
|
293
|
+
* @method del
|
|
294
|
+
* @param {String} i - Waypoint ID to delete
|
|
295
|
+
*/
|
|
296
|
+
const del = (i) => {
|
|
297
|
+
delete points.value[i]
|
|
298
|
+
get()
|
|
299
|
+
}
|
|
110
300
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
301
|
+
/**
|
|
302
|
+
* Move an item in an array from one position to another
|
|
303
|
+
* @method array_move
|
|
304
|
+
* @param {Array} arr - Source array
|
|
305
|
+
* @param {Number} old - Original index
|
|
306
|
+
* @param {Number} new_pos - New index
|
|
307
|
+
* @returns {Array} Modified array
|
|
308
|
+
* @private
|
|
309
|
+
*/
|
|
310
|
+
const array_move = (arr, old, new_pos) => {
|
|
311
|
+
const result = [...arr]
|
|
312
|
+
if (new_pos >= result.length) {
|
|
313
|
+
let k = new_pos - result.length + 1
|
|
314
|
+
while (k--) {
|
|
315
|
+
result.push(undefined)
|
|
121
316
|
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if (i === 0) {
|
|
127
|
-
n = this.$t('map_origin', 'renusify')
|
|
128
|
-
} else if (i === this.points_vals.length - 1) {
|
|
129
|
-
n = this.$t('map_destination', 'renusify')
|
|
130
|
-
}
|
|
131
|
-
return this.$t(['map_point', [n]], 'renusify')
|
|
132
|
-
},
|
|
133
|
-
name(i) {
|
|
134
|
-
return this.pos(i) + ': ' + this.$helper.ifHas(this.modelValue, '', 'waypoints', i, 'name')
|
|
135
|
-
},
|
|
136
|
-
open(i) {
|
|
137
|
-
this.center = Object.values(Object.values(this.markers._layers)[i]._latlng)
|
|
138
|
-
Object.values(this.markers._layers)[i].openPopup()
|
|
139
|
-
},
|
|
140
|
-
del(i) {
|
|
141
|
-
delete this.points[i]
|
|
142
|
-
this.get()
|
|
143
|
-
},
|
|
144
|
-
array_move(arr, old, new_pos) {
|
|
145
|
-
if (new_pos >= arr.length) {
|
|
146
|
-
var k = new_pos - arr.length + 1;
|
|
147
|
-
while (k--) {
|
|
148
|
-
arr.push(undefined);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
arr.splice(new_pos, 0, arr.splice(old, 1)[0]);
|
|
152
|
-
return arr
|
|
153
|
-
},
|
|
154
|
-
to(i, pos) {
|
|
155
|
-
const val = this.array_move(this.points_vals, i, pos)
|
|
156
|
-
const key = this.array_move(this.points_keys, i, pos)
|
|
157
|
-
const way = this.array_move(this.modelValue.waypoints, i, pos)
|
|
158
|
-
let res = {}
|
|
159
|
-
key.forEach((item, i) => {
|
|
160
|
-
res[item] = val[i]
|
|
161
|
-
})
|
|
162
|
-
this.points = res
|
|
163
|
-
this.$emit('update:modelValue', {
|
|
164
|
-
'points': this.points,
|
|
165
|
-
'summary': this.modelValue.summary,
|
|
166
|
-
'waypoints': way
|
|
167
|
-
})
|
|
168
|
-
setTimeout(() => {
|
|
169
|
-
this.get()
|
|
170
|
-
}, 800)
|
|
171
|
-
},
|
|
172
|
-
openLastPopup(n = 0) {
|
|
173
|
-
clearTimeout(this.timeout_id)
|
|
174
|
-
if (Object.values(this.markers._layers)[this.points_vals.length - 1] === undefined) {
|
|
175
|
-
this.timeout_id = setTimeout(() => {
|
|
176
|
-
if (n < 10) {
|
|
177
|
-
this.openLastPopup(n + 1)
|
|
178
|
-
}
|
|
179
|
-
}, 1000)
|
|
180
|
-
return
|
|
181
|
-
}
|
|
182
|
-
Object.values(this.markers._layers)[this.points_vals.length - 1].openPopup()
|
|
183
|
-
},
|
|
184
|
-
add() {
|
|
185
|
-
this.loading = true
|
|
186
|
-
this.points[this.$helper.uniqueId(12)] = Object.values(this.map.getCenter())
|
|
187
|
-
this.get()
|
|
188
|
-
this.openLastPopup()
|
|
189
|
-
this.loading = false
|
|
190
|
-
},
|
|
191
|
-
timeHumanity(seconds) {
|
|
192
|
-
const numyears = Math.floor(seconds / 31536000);
|
|
193
|
-
const numdays = Math.floor((seconds % 31536000) / 86400);
|
|
194
|
-
const numhours = Math.floor(((seconds % 31536000) % 86400) / 3600);
|
|
195
|
-
const numminutes = Math.ceil((((seconds % 31536000) % 86400) % 3600) / 60);
|
|
196
|
-
let res = ''
|
|
197
|
-
if (numyears > 0) {
|
|
198
|
-
res += numyears + ' ' + this.$t("years", 'renusify')
|
|
199
|
-
}
|
|
200
|
-
if (numdays > 0) {
|
|
201
|
-
res += ' ' + numdays + ' ' + this.$t("days", 'renusify')
|
|
202
|
-
}
|
|
203
|
-
if (numhours > 0) {
|
|
204
|
-
res += ' ' + numhours + ' ' + this.$t("hours", 'renusify')
|
|
205
|
-
}
|
|
206
|
-
if (numminutes > 0) {
|
|
207
|
-
res += ' ' + numminutes + ' ' + this.$t("minutes", 'renusify')
|
|
208
|
-
}
|
|
209
|
-
return res
|
|
210
|
-
},
|
|
211
|
-
handleLEvent(e) {
|
|
212
|
-
this.L = e
|
|
213
|
-
},
|
|
214
|
-
handleMapEvent(e) {
|
|
215
|
-
this.map = e
|
|
216
|
-
if (this.points_vals.length > 0) {
|
|
217
|
-
this.center = this.points_vals[0]
|
|
218
|
-
}
|
|
219
|
-
},
|
|
220
|
-
drawMarkers() {
|
|
221
|
-
if (!this.L) {
|
|
222
|
-
return
|
|
223
|
-
}
|
|
224
|
-
if (this.markers) {
|
|
225
|
-
this.map.removeLayer(this.markers)
|
|
226
|
-
}
|
|
227
|
-
const all = []
|
|
228
|
-
const that = this
|
|
229
|
-
|
|
230
|
-
this.points_vals.forEach((point, i) => {
|
|
231
|
-
let icon = this.iconPoint
|
|
232
|
-
if (i === 0) {
|
|
233
|
-
icon = this.iconOrigin
|
|
234
|
-
} else if (i === this.points_vals.length - 1) {
|
|
235
|
-
icon = this.iconDestination
|
|
236
|
-
}
|
|
237
|
-
let m = new this.L.Marker(point, {
|
|
238
|
-
draggable: this.editable,
|
|
239
|
-
index: this.points_keys[i],
|
|
240
|
-
icon: new this.L.icon({
|
|
241
|
-
iconUrl: icon,
|
|
242
|
-
iconSize: [50, 50],
|
|
243
|
-
iconAnchor: [25, 50],
|
|
244
|
-
popupAnchor: [0, -40]
|
|
245
|
-
})
|
|
246
|
-
})
|
|
247
|
-
m.bindPopup(this.$t('map_move_marker', 'renusify') + '<hr>' + this.name(i))
|
|
248
|
-
m.on('drag', function (e) {
|
|
249
|
-
that.points[e.target.options.index] = Object.values(e.latlng)
|
|
250
|
-
});
|
|
251
|
-
m.on('dragend', function (e) {
|
|
252
|
-
that.get()
|
|
253
|
-
});
|
|
254
|
-
all.push(m)
|
|
255
|
-
})
|
|
256
|
-
this.markers = this.L.layerGroup(all);
|
|
257
|
-
this.map.addLayer(this.markers);
|
|
258
|
-
},
|
|
259
|
-
draw() {
|
|
260
|
-
if (this.routing.code !== 'Ok' || !this.L || !this.routing.routes) {
|
|
261
|
-
return
|
|
262
|
-
}
|
|
317
|
+
}
|
|
318
|
+
result.splice(new_pos, 0, result.splice(old, 1)[0])
|
|
319
|
+
return result
|
|
320
|
+
}
|
|
263
321
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
322
|
+
/**
|
|
323
|
+
* Reorder waypoints in the route
|
|
324
|
+
* @method to
|
|
325
|
+
* @param {Number} i - Current waypoint index
|
|
326
|
+
* @param {Number} pos - New position index
|
|
327
|
+
*/
|
|
328
|
+
const to = (i, pos) => {
|
|
329
|
+
const val = array_move(points_vals.value, i, pos)
|
|
330
|
+
const key = array_move(points_keys.value, i, pos)
|
|
331
|
+
const way = array_move(props.modelValue.waypoints || [], i, pos)
|
|
332
|
+
|
|
333
|
+
const res = {}
|
|
334
|
+
key.forEach((item, idx) => {
|
|
335
|
+
res[item] = val[idx]
|
|
336
|
+
})
|
|
337
|
+
|
|
338
|
+
points.value = res
|
|
339
|
+
emit('update:modelValue', {
|
|
340
|
+
'points': points.value,
|
|
341
|
+
'summary': props.modelValue?.summary || {},
|
|
342
|
+
'waypoints': way
|
|
343
|
+
})
|
|
344
|
+
|
|
345
|
+
setTimeout(() => {
|
|
346
|
+
get()
|
|
347
|
+
}, 800)
|
|
348
|
+
}
|
|
278
349
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
l.push(new L.polyline(p, {color: color, weight: 5}))
|
|
296
|
-
}
|
|
297
|
-
})
|
|
298
|
-
this.lines = L.layerGroup(l);
|
|
299
|
-
setTimeout(() => {
|
|
300
|
-
this.map.addLayer(this.lines);
|
|
301
|
-
this.drawMarkers()
|
|
302
|
-
}, 1)
|
|
303
|
-
},
|
|
304
|
-
get() {
|
|
305
|
-
this.$emit('update:modelValue', {
|
|
306
|
-
'points': this.points,
|
|
307
|
-
'summary': {},
|
|
308
|
-
'waypoints': []
|
|
309
|
-
})
|
|
310
|
-
this.drawMarkers()
|
|
311
|
-
if (this.lines) {
|
|
312
|
-
this.map.removeLayer(this.lines);
|
|
313
|
-
}
|
|
314
|
-
if (this.points_vals.length < 2) {
|
|
315
|
-
return
|
|
350
|
+
/**
|
|
351
|
+
* Open the last waypoint's popup after adding a new point
|
|
352
|
+
* @method openLastPopup
|
|
353
|
+
* @param {Number} n - Retry counter (internal use)
|
|
354
|
+
* @private
|
|
355
|
+
*/
|
|
356
|
+
const openLastPopup = (n = 0) => {
|
|
357
|
+
clearTimeout(timeout_id.value)
|
|
358
|
+
|
|
359
|
+
if (!markers.value || !markers.value._layers ||
|
|
360
|
+
Object.values(markers.value._layers)[points_vals.value.length - 1] === undefined) {
|
|
361
|
+
timeout_id.value = setTimeout(() => {
|
|
362
|
+
if (n < 10) {
|
|
363
|
+
openLastPopup(n + 1)
|
|
316
364
|
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
365
|
+
}, 1000)
|
|
366
|
+
return
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
Object.values(markers.value._layers)[points_vals.value.length - 1].openPopup()
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Add a new waypoint at the current map center
|
|
374
|
+
* @method add
|
|
375
|
+
*/
|
|
376
|
+
const add = () => {
|
|
377
|
+
loading.value = true
|
|
378
|
+
points.value[$helper.uniqueId(12)] = Object.values(map.value.getCenter())
|
|
379
|
+
get()
|
|
380
|
+
openLastPopup()
|
|
381
|
+
loading.value = false
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Convert seconds to human-readable time string
|
|
386
|
+
* @method timeHumanity
|
|
387
|
+
* @param {Number} seconds - Time in seconds
|
|
388
|
+
* @returns {String} Formatted time string
|
|
389
|
+
* @example 3661 → "1 hour 1 minute"
|
|
390
|
+
*/
|
|
391
|
+
const timeHumanity = (seconds) => {
|
|
392
|
+
if (!seconds) return ''
|
|
393
|
+
|
|
394
|
+
const numyears = Math.floor(seconds / 31536000)
|
|
395
|
+
const numdays = Math.floor((seconds % 31536000) / 86400)
|
|
396
|
+
const numhours = Math.floor(((seconds % 31536000) % 86400) / 3600)
|
|
397
|
+
const numminutes = Math.ceil((((seconds % 31536000) % 86400) % 3600) / 60)
|
|
398
|
+
|
|
399
|
+
let res = ''
|
|
400
|
+
if (numyears > 0) {
|
|
401
|
+
res += numyears + ' ' + $t("years", 'renusify')
|
|
402
|
+
}
|
|
403
|
+
if (numdays > 0) {
|
|
404
|
+
res += ' ' + numdays + ' ' + $t("days", 'renusify')
|
|
405
|
+
}
|
|
406
|
+
if (numhours > 0) {
|
|
407
|
+
res += ' ' + numhours + ' ' + $t("hours", 'renusify')
|
|
408
|
+
}
|
|
409
|
+
if (numminutes > 0) {
|
|
410
|
+
res += ' ' + numminutes + ' ' + $t("minutes", 'renusify')
|
|
411
|
+
}
|
|
412
|
+
return res.trim()
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Handle Leaflet.js library instance
|
|
417
|
+
* @method handleLEvent
|
|
418
|
+
* @param {Object} e - Leaflet library instance
|
|
419
|
+
*/
|
|
420
|
+
const handleLEvent = (e) => {
|
|
421
|
+
L.value = e
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Handle map instance ready event
|
|
426
|
+
* @method handleMapEvent
|
|
427
|
+
* @param {Object} e - Leaflet map instance
|
|
428
|
+
*/
|
|
429
|
+
const handleMapEvent = (e) => {
|
|
430
|
+
map.value = e
|
|
431
|
+
if (points_vals.value.length > 0) {
|
|
432
|
+
center.value = points_vals.value[0]
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Draw markers for all waypoints on the map
|
|
438
|
+
* @method drawMarkers
|
|
439
|
+
* @private
|
|
440
|
+
*/
|
|
441
|
+
const drawMarkers = () => {
|
|
442
|
+
if (!L.value || !map.value) return
|
|
443
|
+
|
|
444
|
+
if (markers.value) {
|
|
445
|
+
map.value.removeLayer(markers.value)
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
const all = []
|
|
449
|
+
|
|
450
|
+
points_vals.value.forEach((point, i) => {
|
|
451
|
+
let icon = iconPoint.value
|
|
452
|
+
if (i === 0) {
|
|
453
|
+
icon = iconOrigin.value
|
|
454
|
+
} else if (i === points_vals.value.length - 1) {
|
|
455
|
+
icon = iconDestination.value
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
const m = new L.value.Marker(point, {
|
|
459
|
+
draggable: props.editable,
|
|
460
|
+
index: points_keys.value[i],
|
|
461
|
+
icon: new L.value.icon({
|
|
462
|
+
iconUrl: icon,
|
|
463
|
+
iconSize: [50, 50],
|
|
464
|
+
iconAnchor: [25, 50],
|
|
465
|
+
popupAnchor: [0, -40]
|
|
336
466
|
})
|
|
337
|
-
}
|
|
338
|
-
corslite(url, callback) {
|
|
339
|
-
if (typeof window.XMLHttpRequest === 'undefined') {
|
|
340
|
-
return callback(Error('Browser not supported'));
|
|
341
|
-
}
|
|
342
|
-
if (this.req_id) {
|
|
343
|
-
this.req_id.abort()
|
|
344
|
-
}
|
|
345
|
-
let x = new window.XMLHttpRequest();
|
|
467
|
+
})
|
|
346
468
|
|
|
347
|
-
|
|
348
|
-
return status >= 200 && status < 300 || status === 304;
|
|
349
|
-
}
|
|
469
|
+
m.bindPopup($t('map_move_marker', 'renusify') + '<hr>' + name(i))
|
|
350
470
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
x.status === undefined ||
|
|
355
|
-
// modern browsers
|
|
356
|
-
isSuccessful(x.status)) callback.call(x, null, x);
|
|
357
|
-
else callback.call(x, x, null);
|
|
358
|
-
}
|
|
471
|
+
m.on('drag', function (e) {
|
|
472
|
+
points.value[e.target.options.index] = Object.values(e.latlng)
|
|
473
|
+
})
|
|
359
474
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
modifier,
|
|
419
|
-
text
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
for (i = 0; i < legCount; i++) {
|
|
423
|
-
leg = responseRoute.legs[i];
|
|
424
|
-
legNames.push(leg.summary && leg.summary.charAt(0).toUpperCase() + leg.summary.substring(1));
|
|
425
|
-
const lng = leg.steps.length
|
|
426
|
-
for (j = 0; j < lng; j++) {
|
|
427
|
-
step = leg.steps[j];
|
|
428
|
-
geometry = this._decodePolyline(step.geometry);
|
|
429
|
-
result.coordinates.push.apply(result.coordinates, geometry);
|
|
430
|
-
type = this._maneuverToInstructionType(step.maneuver, i === legCount - 1);
|
|
431
|
-
modifier = this._maneuverToModifier(step.maneuver);
|
|
432
|
-
text = [step, {legCount: legCount, legIndex: i}];
|
|
433
|
-
|
|
434
|
-
if (type) {
|
|
435
|
-
if ((i == 0 && step.maneuver.type == 'depart') || step.maneuver.type == 'arrive') {
|
|
436
|
-
waypointIndices.push(index);
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
result.instructions.push({
|
|
440
|
-
type: type,
|
|
441
|
-
distance: step.distance,
|
|
442
|
-
time: step.duration,
|
|
443
|
-
road: step.name,
|
|
444
|
-
direction: this._bearingToDirection(step.maneuver.bearing_after),
|
|
445
|
-
exit: step.maneuver.exit,
|
|
446
|
-
index: index,
|
|
447
|
-
mode: step.mode,
|
|
448
|
-
modifier: modifier,
|
|
449
|
-
text: text
|
|
450
|
-
});
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
index += geometry.length;
|
|
475
|
+
m.on('dragend', function () {
|
|
476
|
+
get()
|
|
477
|
+
})
|
|
478
|
+
|
|
479
|
+
all.push(m)
|
|
480
|
+
})
|
|
481
|
+
|
|
482
|
+
markers.value = L.value.layerGroup(all)
|
|
483
|
+
map.value.addLayer(markers.value)
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Draw route lines on the map based on calculated route
|
|
488
|
+
* @method draw
|
|
489
|
+
* @private
|
|
490
|
+
*/
|
|
491
|
+
const draw = () => {
|
|
492
|
+
if (routing.value.code !== 'Ok' || !L.value || !routing.value.routes) {
|
|
493
|
+
return
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
const alts = {
|
|
497
|
+
'points': points.value,
|
|
498
|
+
'routes': [],
|
|
499
|
+
'summary': {'name': '', 'time': 0, 'distance': 0}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
const routesLength = Object.values(routing.value.routes).length
|
|
503
|
+
for (let i = 0; i < routesLength; i++) {
|
|
504
|
+
const route = convertRoute(routing.value.routes[i])
|
|
505
|
+
alts.routes.push(route)
|
|
506
|
+
alts.summary.name = route.name
|
|
507
|
+
alts.summary.time += parseFloat(route.summary.total_time) * props.marginTime
|
|
508
|
+
alts.summary.distance += parseFloat(route.summary.total_distance)
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
alts.waypoints = routing.value.waypoints || []
|
|
512
|
+
alts.waypoints.forEach((item) => {
|
|
513
|
+
delete item.hint
|
|
514
|
+
delete item.location
|
|
515
|
+
})
|
|
516
|
+
|
|
517
|
+
emit('update:modelValue', {
|
|
518
|
+
'points': alts.points,
|
|
519
|
+
'summary': alts.summary,
|
|
520
|
+
'waypoints': alts.waypoints
|
|
521
|
+
})
|
|
522
|
+
|
|
523
|
+
const l = []
|
|
524
|
+
if (alts.routes[0]?.waypointIndices) {
|
|
525
|
+
alts.routes[0].waypointIndices.forEach((item, i) => {
|
|
526
|
+
if (i < alts.routes[0].waypointIndices.length - 1) {
|
|
527
|
+
const p = alts.routes[0].coordinates.slice(item, alts.routes[0].waypointIndices[i + 1])
|
|
528
|
+
let color = 'red'
|
|
529
|
+
if (props.color[i]) {
|
|
530
|
+
color = props.color[i]
|
|
531
|
+
} else if (props.color.length > 0) {
|
|
532
|
+
color = i % 2 === 0 ? props.color[0] : (props.color[1] || props.color[0])
|
|
454
533
|
}
|
|
455
|
-
}
|
|
456
534
|
|
|
457
|
-
|
|
458
|
-
if (!hasSteps) {
|
|
459
|
-
result.coordinates = this._decodePolyline(responseRoute.geometry);
|
|
460
|
-
} else {
|
|
461
|
-
result.waypointIndices = waypointIndices;
|
|
535
|
+
l.push(new L.value.polyline(p, {color: color, weight: 5}))
|
|
462
536
|
}
|
|
537
|
+
})
|
|
538
|
+
}
|
|
463
539
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
540
|
+
lines.value = L.value.layerGroup(l)
|
|
541
|
+
setTimeout(() => {
|
|
542
|
+
if (map.value && lines.value) {
|
|
543
|
+
map.value.addLayer(lines.value)
|
|
544
|
+
}
|
|
545
|
+
drawMarkers()
|
|
546
|
+
}, 1)
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* Calculate route by fetching from OSRM API
|
|
551
|
+
* @method get
|
|
552
|
+
*/
|
|
553
|
+
const get = () => {
|
|
554
|
+
emit('update:modelValue', {
|
|
555
|
+
'points': points.value,
|
|
556
|
+
'summary': {},
|
|
557
|
+
'waypoints': []
|
|
558
|
+
})
|
|
559
|
+
|
|
560
|
+
drawMarkers()
|
|
561
|
+
|
|
562
|
+
if (lines.value && map.value) {
|
|
563
|
+
map.value.removeLayer(lines.value)
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
if (points_vals.value.length < 2) {
|
|
567
|
+
return
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
let url = `https://routing.openstreetmap.de/routed-${props.by}/route/v1/driving/`
|
|
571
|
+
points_vals.value.forEach(point => {
|
|
572
|
+
url += point[1] + ',' + point[0] + ';'
|
|
573
|
+
})
|
|
574
|
+
url = url.substr(0, url.length - 1) + '?overview=false&steps=true'
|
|
575
|
+
|
|
576
|
+
corslite(url, function (err, resp) {
|
|
577
|
+
if (!err) {
|
|
578
|
+
try {
|
|
579
|
+
routing.value = JSON.parse(resp.responseText)
|
|
580
|
+
draw()
|
|
581
|
+
} catch (e) {
|
|
582
|
+
console.error('Error parsing OSRM response: ' + e.toString())
|
|
501
583
|
}
|
|
584
|
+
} else {
|
|
585
|
+
console.error('HTTP request failed: ' + err.type +
|
|
586
|
+
(err.target && err.target.status ? ' HTTP ' + err.target.status + ': ' + err.target.statusText : ''))
|
|
587
|
+
}
|
|
588
|
+
})
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Cross-origin request helper function
|
|
593
|
+
* @method corslite
|
|
594
|
+
* @param {String} url - Request URL
|
|
595
|
+
* @param {Function} callback - Callback function (err, response)
|
|
596
|
+
* @returns {XMLHttpRequest} XHR object
|
|
597
|
+
* @private
|
|
598
|
+
*/
|
|
599
|
+
const corslite = (url, callback) => {
|
|
600
|
+
if (typeof window.XMLHttpRequest === 'undefined') {
|
|
601
|
+
return callback(Error('Browser not supported'))
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
if (req_id.value) {
|
|
605
|
+
req_id.value.abort()
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
const x = new window.XMLHttpRequest()
|
|
502
609
|
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
// Reset shift, result, and byte
|
|
523
|
-
byte = null;
|
|
524
|
-
shift = 0;
|
|
525
|
-
result = 0;
|
|
526
|
-
|
|
527
|
-
do {
|
|
528
|
-
byte = str.charCodeAt(index++) - 63;
|
|
529
|
-
result |= (byte & 0x1f) << shift;
|
|
530
|
-
shift += 5;
|
|
531
|
-
} while (byte >= 0x20);
|
|
532
|
-
|
|
533
|
-
latitude_change = ((result & 1) ? ~(result >> 1) : (result >> 1));
|
|
534
|
-
|
|
535
|
-
shift = result = 0;
|
|
536
|
-
|
|
537
|
-
do {
|
|
538
|
-
byte = str.charCodeAt(index++) - 63;
|
|
539
|
-
result |= (byte & 0x1f) << shift;
|
|
540
|
-
shift += 5;
|
|
541
|
-
} while (byte >= 0x20);
|
|
542
|
-
|
|
543
|
-
longitude_change = ((result & 1) ? ~(result >> 1) : (result >> 1));
|
|
544
|
-
|
|
545
|
-
lat += latitude_change;
|
|
546
|
-
lng += longitude_change;
|
|
547
|
-
|
|
548
|
-
coordinates.push([lat / factor, lng / factor]);
|
|
610
|
+
function isSuccessful(status) {
|
|
611
|
+
return status >= 200 && status < 300 || status === 304
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
function loaded() {
|
|
615
|
+
if (x.status === undefined || isSuccessful(x.status)) {
|
|
616
|
+
callback.call(x, null, x)
|
|
617
|
+
} else {
|
|
618
|
+
callback.call(x, x, null)
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
if ('onload' in x) {
|
|
623
|
+
x.onload = loaded
|
|
624
|
+
} else {
|
|
625
|
+
x.onreadystatechange = function readystate() {
|
|
626
|
+
if (x.readyState === 4) {
|
|
627
|
+
loaded()
|
|
549
628
|
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
x.timeout = 7000
|
|
633
|
+
x.onerror = function error(e) {
|
|
634
|
+
callback.call(this, e || true, null)
|
|
635
|
+
}
|
|
550
636
|
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
637
|
+
x.onprogress = function () {
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
x.ontimeout = function (e) {
|
|
641
|
+
callback.call(this, e, null)
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
x.onabort = function (e) {
|
|
645
|
+
callback.call(this, e, null)
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
x.open('GET', url, true)
|
|
649
|
+
x.send(null)
|
|
650
|
+
|
|
651
|
+
req_id.value = x
|
|
652
|
+
return x
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
* Convert OSRM API response to internal route format
|
|
657
|
+
* @method convertRoute
|
|
658
|
+
* @param {Object} responseRoute - OSRM route object
|
|
659
|
+
* @returns {Object} Formatted route object
|
|
660
|
+
* @private
|
|
661
|
+
*/
|
|
662
|
+
const convertRoute = (responseRoute) => {
|
|
663
|
+
const result = {
|
|
664
|
+
name: '',
|
|
665
|
+
coordinates: [],
|
|
666
|
+
instructions: [],
|
|
667
|
+
summary: {
|
|
668
|
+
total_distance: responseRoute.distance,
|
|
669
|
+
total_time: responseRoute.duration
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
const legNames = []
|
|
674
|
+
const waypointIndices = []
|
|
675
|
+
let index = 0
|
|
676
|
+
const legCount = responseRoute.legs.length
|
|
677
|
+
const hasSteps = legCount > 0 && responseRoute.legs[0].steps.length > 0
|
|
678
|
+
|
|
679
|
+
for (let i = 0; i < legCount; i++) {
|
|
680
|
+
const leg = responseRoute.legs[i]
|
|
681
|
+
legNames.push(leg.summary && leg.summary.charAt(0).toUpperCase() + leg.summary.substring(1))
|
|
682
|
+
|
|
683
|
+
const stepCount = leg.steps.length
|
|
684
|
+
for (let j = 0; j < stepCount; j++) {
|
|
685
|
+
const step = leg.steps[j]
|
|
686
|
+
const geometry = decodePolyline(step.geometry)
|
|
687
|
+
result.coordinates.push(...geometry)
|
|
688
|
+
|
|
689
|
+
const type = maneuverToInstructionType(step.maneuver, i === legCount - 1)
|
|
690
|
+
const modifier = maneuverToModifier(step.maneuver)
|
|
691
|
+
const text = [step, {legCount: legCount, legIndex: i}]
|
|
692
|
+
|
|
693
|
+
if (type) {
|
|
694
|
+
if ((i == 0 && step.maneuver.type == 'depart') || step.maneuver.type == 'arrive') {
|
|
695
|
+
waypointIndices.push(index)
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
result.instructions.push({
|
|
699
|
+
type: type,
|
|
700
|
+
distance: step.distance,
|
|
701
|
+
time: step.duration,
|
|
702
|
+
road: step.name,
|
|
703
|
+
direction: bearingToDirection(step.maneuver.bearing_after),
|
|
704
|
+
exit: step.maneuver.exit,
|
|
705
|
+
index: index,
|
|
706
|
+
mode: step.mode,
|
|
707
|
+
modifier: modifier,
|
|
708
|
+
text: text
|
|
709
|
+
})
|
|
560
710
|
}
|
|
561
711
|
|
|
562
|
-
|
|
712
|
+
index += geometry.length
|
|
563
713
|
}
|
|
564
714
|
}
|
|
715
|
+
|
|
716
|
+
result.name = legNames.join(', ')
|
|
717
|
+
if (!hasSteps) {
|
|
718
|
+
result.coordinates = decodePolyline(responseRoute.geometry)
|
|
719
|
+
} else {
|
|
720
|
+
result.waypointIndices = waypointIndices
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
return result
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
/**
|
|
727
|
+
* Convert bearing (degrees) to cardinal direction
|
|
728
|
+
* @method bearingToDirection
|
|
729
|
+
* @param {Number} bearing - Bearing in degrees (0-360)
|
|
730
|
+
* @returns {String} Cardinal direction (N, NE, E, SE, S, SW, W, NW)
|
|
731
|
+
* @private
|
|
732
|
+
*/
|
|
733
|
+
const bearingToDirection = (bearing) => {
|
|
734
|
+
const oct = Math.round(bearing / 45) % 8
|
|
735
|
+
return ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'][oct]
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
/**
|
|
739
|
+
* Convert OSRM maneuver type to instruction type
|
|
740
|
+
* @method maneuverToInstructionType
|
|
741
|
+
* @param {Object} maneuver - OSRM maneuver object
|
|
742
|
+
* @param {Boolean} lastLeg - Whether this is the last leg of the route
|
|
743
|
+
* @returns {String} Instruction type
|
|
744
|
+
* @private
|
|
745
|
+
*/
|
|
746
|
+
const maneuverToInstructionType = (maneuver, lastLeg) => {
|
|
747
|
+
switch (maneuver.type) {
|
|
748
|
+
case 'new name':
|
|
749
|
+
return 'continue'
|
|
750
|
+
case 'depart':
|
|
751
|
+
return 'head'
|
|
752
|
+
case 'arrive':
|
|
753
|
+
return lastLeg ? 'destinationReached' : 'waypointReached'
|
|
754
|
+
case 'roundabout':
|
|
755
|
+
case 'rotary':
|
|
756
|
+
return 'roundabout'
|
|
757
|
+
case 'merge':
|
|
758
|
+
case 'fork':
|
|
759
|
+
case 'on ramp':
|
|
760
|
+
case 'off ramp':
|
|
761
|
+
case 'end of road':
|
|
762
|
+
return maneuver.type.replaceAll(' ', '_')
|
|
763
|
+
default:
|
|
764
|
+
return maneuver.modifier?.replaceAll(' ', '_') || ''
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
/**
|
|
769
|
+
* Convert OSRM maneuver modifier to internal modifier format
|
|
770
|
+
* @method maneuverToModifier
|
|
771
|
+
* @param {Object} maneuver - OSRM maneuver object
|
|
772
|
+
* @returns {String} Modified instruction modifier
|
|
773
|
+
* @private
|
|
774
|
+
*/
|
|
775
|
+
const maneuverToModifier = (maneuver) => {
|
|
776
|
+
let modifier = maneuver.modifier
|
|
777
|
+
|
|
778
|
+
switch (maneuver.type) {
|
|
779
|
+
case 'merge':
|
|
780
|
+
case 'fork':
|
|
781
|
+
case 'on ramp':
|
|
782
|
+
case 'off ramp':
|
|
783
|
+
case 'end of road':
|
|
784
|
+
modifier = leftOrRight(modifier)
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
return modifier && modifier.replaceAll(' ', '_')
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
/**
|
|
791
|
+
* Determine left or right direction from modifier string
|
|
792
|
+
* @method leftOrRight
|
|
793
|
+
* @param {String} d - Direction modifier string
|
|
794
|
+
* @returns {String} 'left' or 'right'
|
|
795
|
+
* @private
|
|
796
|
+
*/
|
|
797
|
+
const leftOrRight = (d) => {
|
|
798
|
+
return d?.indexOf('left') >= 0 ? 'left' : 'right'
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* Decode polyline string to coordinate array (OSRM geometry format)
|
|
803
|
+
* @method polyline_decode
|
|
804
|
+
* @param {String} str - Encoded polyline string
|
|
805
|
+
* @param {Number} precision - Coordinate precision (default: 5)
|
|
806
|
+
* @returns {Array<Array<Number>>} Array of [lat, lng] coordinates
|
|
807
|
+
* @private
|
|
808
|
+
*/
|
|
809
|
+
const polyline_decode = (str, precision) => {
|
|
810
|
+
let index = 0,
|
|
811
|
+
lat = 0,
|
|
812
|
+
lng = 0,
|
|
813
|
+
coordinates = [],
|
|
814
|
+
shift = 0,
|
|
815
|
+
result = 0,
|
|
816
|
+
byte = null,
|
|
817
|
+
latitude_change,
|
|
818
|
+
longitude_change,
|
|
819
|
+
factor = Math.pow(10, precision || 5)
|
|
820
|
+
|
|
821
|
+
while (index < str.length) {
|
|
822
|
+
// Reset shift, result, and byte
|
|
823
|
+
byte = null
|
|
824
|
+
shift = 0
|
|
825
|
+
result = 0
|
|
826
|
+
|
|
827
|
+
do {
|
|
828
|
+
byte = str.charCodeAt(index++) - 63
|
|
829
|
+
result |= (byte & 0x1f) << shift
|
|
830
|
+
shift += 5
|
|
831
|
+
} while (byte >= 0x20)
|
|
832
|
+
|
|
833
|
+
latitude_change = ((result & 1) ? ~(result >> 1) : (result >> 1))
|
|
834
|
+
|
|
835
|
+
shift = result = 0
|
|
836
|
+
|
|
837
|
+
do {
|
|
838
|
+
byte = str.charCodeAt(index++) - 63
|
|
839
|
+
result |= (byte & 0x1f) << shift
|
|
840
|
+
shift += 5
|
|
841
|
+
} while (byte >= 0x20)
|
|
842
|
+
|
|
843
|
+
longitude_change = ((result & 1) ? ~(result >> 1) : (result >> 1))
|
|
844
|
+
|
|
845
|
+
lat += latitude_change
|
|
846
|
+
lng += longitude_change
|
|
847
|
+
|
|
848
|
+
coordinates.push([lat / factor, lng / factor])
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
return coordinates
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
/**
|
|
855
|
+
* Decode polyline and convert to Leaflet LatLng objects
|
|
856
|
+
* @method decodePolyline
|
|
857
|
+
* @param {String} routeGeometry - Encoded polyline geometry string
|
|
858
|
+
* @returns {Array<L.LatLng>} Array of Leaflet LatLng objects
|
|
859
|
+
* @private
|
|
860
|
+
*/
|
|
861
|
+
const decodePolyline = (routeGeometry) => {
|
|
862
|
+
const cs = polyline_decode(routeGeometry, 5),
|
|
863
|
+
result = []
|
|
864
|
+
|
|
865
|
+
const lng = cs.length
|
|
866
|
+
for (let i = lng - 1; i >= 0; i--) {
|
|
867
|
+
result[i] = L.value.latLng(cs[i])
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
return result
|
|
565
871
|
}
|
|
566
872
|
</script>
|
|
873
|
+
|
|
567
874
|
<style lang="scss">
|
|
568
875
|
@use "../../style/variables/base";
|
|
569
876
|
|