בתרחיש הזה מתבצעת אופטימיזציה של סדר העצירות שמוקצות לרכב באמצעות פרמטרים פשוטים של עלות. זהו המצב הפשוט ביותר לביצוע אופטימיזציה של מסלולים, והוא מבטיח שהדיווח יתבצע בכל העצירות במסגרת הזמן שצוינה.
הדוגמה הבאה ממחישה תרחיש בסיסי עם רכב אחד ושלוש משלוחים, שהגיעו כולן ממיקום אחד שנקרא depot.
להצגת בקשה לדוגמה
{ "populatePolylines": true, "populateTransitionPolylines": true, "model": { "globalStartTime": "2023-01-13T16:00:00-08:00", "globalEndTime": "2023-01-14T16:00:00-08:00", "shipments": [ { "deliveries": [ { "arrivalLocation": { "latitude": 37.789456, "longitude": -122.390192 }, "duration": "250s" } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ] }, { "deliveries": [ { "arrivalLocation": { "latitude": 37.789116, "longitude": -122.395080 }, "duration": "250s" } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ] }, { "deliveries": [ { "arrivalLocation": { "latitude": 37.795242, "longitude": -122.399347 }, "duration": "250s" } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ] } ], "vehicles": [ { "endLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "startLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "costPerKilometer": 10.0, "costPerHour": 40.0 } ] } }
שדות של בקשות לאופטימיזציה של מסלול
כפי שצוין ב-Overview, המאפיינים החשובים ביותר של בקשות האופטימיזציה של מסלולים הם vehicles
ו-shipments
.
בנוסף לרכב ולמשלוחים, הבקשה כוללת את השדות הבאים:
קווים פוליגוניים
populatePolylines
ו-populateTransitionPolylines
מציינים אם האופטימיזציה של מסלולים צריכה להחזיר קווים פוליגוניים.
השירות מקודד את Polylines באמצעות קודק פוליגונים של מפות JS, שמייצג נתונים פוליגוניים בינאריים באמצעות תווי ASCII ניתנים להדפסה. אפשר להשתמש ב-Interactive Polyline Encoder Tool כדי להמחיש את הנתיבים שמחושבים על ידי Route Optimization. בדוגמה במדריך הזה, הערכים populatePolylines
ו-populateTransitionPolylines
מוגדרים כ-True, אבל במדריכים אחרים אפשר להגדיר את הערכים כ-FALSE כדי לצמצם את גודל התשובה.
לתיאור של פורמט הקידוד, ראו את המאמר פורמט אלגוריתם פוליגוני מקודד.
מגבלות זמן גלובליות
model.globalStartTime
ו-model.globalEndTime
מוגדרים לפרק זמן שרירותי של 24 שעות. כך קל יותר לפרש את חותמות הזמן של הפלט.
ביקור במיקומים
בבקשה לדוגמה נעשה שימוש רק ב-model.shipments[].pickups[].arrivalLocation
וב-model.shipments[].deliveries[].arrivalLocation
. יש גם נכס departureLocation
למצבים שבהם הרכב יוצא מנקודה שונה מנקודה שבה הוא מגיע, כמו מתחם חניה עם כניסה בצד אחד של המבנה ויציאה בנקודה אחרת. במדריך הזה ובמדריכים הבאים, נקודות ההגעה והיציאה הן זהות.
אמצעי ההגעה והיציאה ב-waypoint
זמינים גם כחלופה לתחנת latLng
.
שדות Waypoint
תומכים בשימוש במזהי מקומות ב-Google כחלופה ל-LatLng
, ואפשר גם לציין כותרות של רכבים. למידע נוסף, אפשר לעיין במסמכי התיעוד (REST, gRPC).
מגבלות בדוגמה
תרחיש זה מגביל את האופטימיזציה בכמה דרכים:
- יש להשלים את כל הפעילות בין זמני ההתחלה והסיום הגלובליים. בתרחיש הזה, זמני ההתחלה והסיום הם מגבלה דחופה מאוד בגלל קרבה גיאוגרפית למשלוחים וחלון הזמן הגלובלי הרחב.
- צריך להשלים את כל המשלוחים. זו התנהגות ברירת המחדל כשעלויות הקנס לא מצוינות ב-
shipments
. - ההגדרות
costPerKilometer
וגםcostPerHour
מוגדרות ברכב.
העלויות מפורטות בקטע פרמטרים של מודל עלות.
מאפייני תגובה של אופטימיזציה של מסלול
צפייה בתשובה לבקשה לדוגמה
{ "routes": [ { "vehicleStartTime": "2023-01-14T00:00:00Z", "vehicleEndTime": "2023-01-14T00:36:41Z", "visits": [ { "shipmentIndex": 2, "isPickup": true, "startTime": "2023-01-14T00:00:00Z", "detour": "0s" }, { "shipmentIndex": 1, "isPickup": true, "startTime": "2023-01-14T00:02:30Z", "detour": "150s" }, { "isPickup": true, "startTime": "2023-01-14T00:05:00Z", "detour": "300s" }, { "startTime": "2023-01-14T00:11:25Z", "detour": "0s" }, { "shipmentIndex": 1, "startTime": "2023-01-14T00:19:29Z", "detour": "503s" }, { "shipmentIndex": 2, "startTime": "2023-01-14T00:29:02Z", "detour": "1324s" } ], "transitions": [ { "travelDuration": "0s", "waitDuration": "0s", "totalDuration": "0s", "startTime": "2023-01-14T00:00:00Z", "routePolyline": {} }, { "travelDuration": "0s", "waitDuration": "0s", "totalDuration": "0s", "startTime": "2023-01-14T00:02:30Z", "routePolyline": {} }, { "travelDuration": "0s", "waitDuration": "0s", "totalDuration": "0s", "startTime": "2023-01-14T00:05:00Z", "routePolyline": {} }, { "travelDuration": "235s", "travelDistanceMeters": 795, "waitDuration": "0s", "totalDuration": "235s", "startTime": "2023-01-14T00:07:30Z", "routePolyline": { "points": "kvteFtfjVAA?C?C@C?A?C@AFMj@s@JKb@k@Zc@LSjA}ARWDGdAxAdAvAXa@@k@AsA\\c@FKp@_A\\c@Ze@fA{ALSFGd@o@rAgBB{BZc@" } }, { "travelDuration": "234s", "travelDistanceMeters": 793, "waitDuration": "0s", "totalDuration": "234s", "startTime": "2023-01-14T00:15:35Z", "routePolyline": { "points": "cwseFti_jVRWj@w@x@eAHLNRHJbApAHLX\\V^?@hA~AT\\PVFFDHDFJNp@~@NRLNNTFFUZIJY^Y^g@p@[`@KP{@fAEFSXe@l@c@h@WZY\\?BELk@v@MNa@l@" } }, { "travelDuration": "323s", "travelDistanceMeters": 1204, "waitDuration": "0s", "totalDuration": "323s", "startTime": "2023-01-14T00:23:39Z", "routePolyline": { "points": "cuseFhjVSTY`@Yb@GHEDIJEF]f@IJi@r@oAbBeCfDKLaApAKNQVIPKPCDQJIBIBM@iAJeALqBVC@C?A?QBYDI@C?_@Dc@FO@a@FDp@HfAHvABVDl@Dj@PpCQDiALsALAQASKwAOgBEe@COCYEa@Es@Eg@" } }, { "travelDuration": "209s", "travelDistanceMeters": 665, "waitDuration": "0s", "totalDuration": "209s", "startTime": "2023-01-14T00:33:12Z", "routePolyline": { "points": "{zteFxbajV?CAYEc@AMC_@AOAK?E?CCWAOAKCe@CY?WScDEm@d@EFA\\ENCB?XEVC^E`@EhBUVCNEB?@?\\Er@IMUe@k@k@w@AAMQa@i@SWQWMQi@u@AC?A" } } ], "routePolyline": { "points": "kvteFtfjVAA?C?C@C?A?C@AFMj@s@JKb@k@Zc@LSjA}ARWDGdAxAdAvAXa@@k@AsA\\c@FKp@_A\\c@Ze@fA{ALSFGd@o@rAgBB{BZc@RWj@w@x@eAHLNRHJbApAHLX\\V^?@hA~AT\\PVFFDHDFJNp@~@NRLNNTFFUZIJY^Y^g@p@[@KP{@fAEFSXe@l@c@h@WZY\\?BELk@v@MNa@l@STY@Yb@GHEDIJEF]f@IJi@r@oAbBeCfDKLaApAKNQVIPKPCDQJIBIBM@iAJeALqBVC@C?A?QBYDI@C?_@Dc@FO@a@FDp@HfAHvABVDl@Dj@PpCQDiALsALAQASKwAOgBEe@COCYEa@Es@Eg@?CAYEc@AMC_@AOAK?E?CCWAOAKCe@CY?WScDEm@d@EFA\\ENCB?XEVC^E`@EhBUVCNEB?@?\\Er@IMUe@k@k@w@AAMQa@i@SWQWMQi@u@AC?A" }, "metrics": { "performedShipmentCount": 3, "travelDuration": "1001s", "waitDuration": "0s", "delayDuration": "0s", "breakDuration": "0s", "visitDuration": "1200s", "totalDuration": "2201s", "travelDistanceMeters": 3457 }, "travelSteps": [ { "duration": "0s", "routePolyline": {} }, { "duration": "0s", "routePolyline": {} }, { "duration": "0s", "routePolyline": {} }, { "duration": "227s", "distanceMeters": 794, "routePolyline": { "points": "kvteFtfjVAA?C?C@C?A?C@AFMj@s@JKb@k@Zc@LSjA}ARWDGdAxAdAvAXa@@k@AsA\\c@FKp@_A\\c@Ze@fA{ALSFGd@o@rAgBB{BZc@" } }, { "duration": "233s", "distanceMeters": 791, "routePolyline": { "points": "cwseFti_jVRWj@w@x@eAHLNRHJbApAHLX\\V^?@hA~AT\\PVFFDHDFJNp@~@NRLNNTFFUZIJY^Y^g@p@[`@KP{@fAEFSXe@l@c@h@WZY\\?BELk@v@MNa@l@" } }, { "duration": "322s", "distanceMeters": 1205, "routePolyline": { "points": "cuseFhjVSTY`@Yb@GHEDIJEF]f@IJi@r@oAbBeCfDKLaApAKNQVIPKPCDQJIBIBM@iAJeALqBVC@C?A?QBYDI@C?_@Dc@FO@a@FDp@HfAHvABVDl@Dj@PpCQDiALsALAQASKwAOgBEe@COCYEa@Es@Eg@" } }, { "duration": "208s", "distanceMeters": 666, "routePolyline": { "points": "{zteFxbajV?CAYEc@AMC_@AOAK?E?CCWAOAKCe@CY?WScDEm@d@EFA\\ENCB?XEVC^E`@EhBUVCNEB?@?\\Er@IMUe@k@k@w@AAMQa@i@SWQWMQi@u@AC?A" } } ], "vehicleDetour": "2201s", "routeCosts": { "model.vehicles.cost_per_hour": 24.455555555555556, "model.vehicles.cost_per_kilometer": 34.57 }, "routeTotalCost": 59.025555555555556 } ], "totalCost": 59.025555555555556, "metrics": { "aggregatedRouteMetrics": { "performedShipmentCount": 3, "travelDuration": "1001s", "waitDuration": "0s", "delayDuration": "0s", "breakDuration": "0s", "visitDuration": "1200s", "totalDuration": "2201s", "travelDistanceMeters": 3457 }, "usedVehicleCount": 1, "earliestVehicleStartTime": "2023-01-14T00:00:00Z", "latestVehicleEndTime": "2023-01-14T00:36:41Z", "totalCost": 59.025555555555556, "costs": { "model.vehicles.cost_per_kilometer": 34.57, "model.vehicles.cost_per_hour": 24.455555555555556 } } }
התשובה של מסלול האופטימיזציה כוללת את השדה routes
ברמה העליונה שמייצג את המסלולים המוצעים, עם מסלול אחד לכל רכב. מכיוון שבבקשה לדוגמה במדריך הזה מצוין רק רכב אחד, routes
כולל הודעת ShipmentRoute
אחת.
ShipmentRoute
מלונות
שני המאפיינים החשובים ביותר לסוג ההודעה ShipmentRoute
הם visits
ו-transitions
.
כל Visit
מייצג את השלמת האיסוף או המסירה מאחת ה-VisitRequest
של הודעת הבקשה. בביקור תוקצה עבודה בפועל
על ידי כלי רכב במקום מסוים ובזמן מסוים.
כל Transition
מייצג את כלי הרכב שנוסע ממיקום אחד לאחר. יכולים להתרחש מעברים בין שתי נקודות ההתחלה של הרכב, המיקום של הביקור ונקודת הקצה של הרכב.
כדי לשחזר את המסלול המלא של הרכב, צריך לשלב את visits
עם transitions
.ShipmentRoute
שילוב השדות להתקדמות הפעילות ברכב נראה כך:
request.vehicles[0].startLocation -> transitions[0] -> visits[0] ->
transitions[1] -> visits[1] -> transitions[2] -> ... -> visits[3] ->
transitions[4] -> request.vehicles[0].endLocation
ב-ShipmentRoute
יש תמיד transitions
אחד יותר מאשר visits
, כי הרכב צריך לנסוע מנקודת ההתחלה ועד לביקור הראשון בתחילת המסלול ומהביקור האחרון במיקום הסיום שלו בסוף המסלול. אם הרכב לא כולל נקודות התחלה או סיום, עדיין יהיה transitions
יותר מ-visits
, כי המיקום של הביקור הראשון או האחרון משמש כנקודת ההתחלה או הסיום של הרכב, בהתאמה.
בדוגמה זו, בשלושת הביקורים הראשונים לאיסוף יש מעברים ביניהם עם אפס מרחק ומשך זמן, כי כל שלושת האיסוףיות חולקות את אותו המיקום בבקשה.
לפרטים נוספים, עיינו במסמכי התיעוד של ShipmentRoute
(REST, gRPC).
אופטימיזציה פשוטה של סדר ציון דרך
כפי שהדוגמה הזו ממחישה, האופטימיזציה של מסלול יוצרת מודלים של ביקורים כמאפיינים של משלוחים, ולא כוללת התייחסות לציוני דרך או לעצירות כישות עצמאית. עם זאת, אפשר לייצג תחנות או ציוני דרך כמשלוחים עם VisitRequest
אחד בדיוק כאיסוף או במשלוח. עדיין צריך להקצות לרכב costPerHour
או costPerKilometer
כדי שכלי האופטימיזציה יוכל למצוא מסלול אופטימלי (במקום למצוא כל מסלול אפשרי).