ルート違反をマッピングし、ジョブ割り当てを制御する

通行止め、回避エリア、物理的な制限などのルート違反により、最適化アルゴリズムが旅程内のジョブを未割り当てのままにしてしまうことがあります。ただし、運行管理オペレーターは、どの違反を許容するか、また違反をどのように追跡するかについてきめ細かな制御を必要とすることがよくあります。たとえば、配送運行管理オペレーターは、緊急の納期に間に合わせるためにトラックが通行止めになった橋を渡ることを許可したい一方で、安全上の懸念から高さ制限によって通行が妨げられているジョブの割り当ては拒否したい場合があり、どの停車地にルーティング上のリスクがあるかを把握する必要があります。

violationMappingは、違反を名前付きカテゴリーに分類することでこの問題を解決し、ソリューション内で異なる違反タイプの影響を受けるジョブを区別できるようにします。付随するプロパティであるignoreRouteViolationsは、最適化アルゴリズムに対してどのカテゴリーを到達可能として扱うかを指示します。これら2つのプロパティを組み合わせることで、マッピングした違反タイプごとに、割り当てとリスク追跡を制御できるようになります。

次の違反タイプをマッピングできます。

  • traffic:工事、事故、その他の交通関連事象などにより、一時的または恒久的に道路が閉鎖されることによって引き起こされる違反。タイプcarおよびtruckの車両プロファイルでサポートされています。
  • avoidAreasAndSegments:私道やトンネルなどの施設など、回避すべきと明示的にマークされたエリアまたは道路区間。タイプcarおよびtruckの車両プロファイルでサポートされています。
  • restriction:高さ、重量、幅の制限など、車両がジョブの場所にアクセスすることを妨げる物理的または法的な制約。truckの車両プロファイルでのみサポートされています。
📘

これは開発中のアルファ機能です。問題のconfigurationオブジェクト内のexperimentalFeatures配列に"violationMapping"を追加することで有効化できます。

詳細については、「テスト段階の機能の詳細」を参照してください。

違反マッピングの仕組み

violationMapping機能は、次の2つのステップのメカニズムで動作します。

  • ステップ1violationMappingが、違反を名前付きカテゴリー (category1またはcategory2) に分類します。
  • ステップ2ignoreRouteViolationsが、そのカテゴリーに属する停車地を到達可能として扱うかどうかを最適化アルゴリズムに指示します。

次のスニペットは、車両プロファイル内のviolationMappingの最小構成を示しています。単一の違反タイプを単一のカテゴリーにマッピングし、そのカテゴリーの違反を無視するようアルゴリズムに指示します。

"profiles": [
  {
    "name": "my_truck",
    "type": "truck",
    "violationMapping": [
      {
        "category": "category1",
        "type": "traffic"
      }
    ],
    "ignoreRouteViolations": ["category1"]
  }
]

前の例が示すように、violationMappingignoreRouteViolationsの両方はfleet.profiles配列内で設定されます。この例は、違反マッピングの基本的なパターンを示しています。この記事で紹介するより高度な設定はすべて、この同じパターンに従い、その上にいくつかの要素が追加されています。この具体的な例は次のようになっています。

  • categoryは、バケットのラベル
  • typeは、マッピングされている違反クラス
  • ignoreRouteViolationsは、categoryバケットに対して無視動作を有効にするリスト

次のフローは、前の例に基づいて、2つのステップがどのように連携し、停車地が割り当てられるか、あるいは割り当てられないかがどのように決定されるかを示しています。

flowchart LR
  A([<b>Route violation<br/>occurs</b>]) ==> B["<b>Step 1</b><br/>violationMapping<br/>classifies it into<br/><b>category1</b>"]
  B ==> C{"<b>Step 2</b><br/>Is category1 listed<br/>in ignoreRouteViolations?"}
  C -- Yes --> D([Stop <b>assigned</b><br/>with unreachableLocation<br/>notice])
  C -- No --> E([Stop <b>unassigned</b><br/>with REACHABLE_CONSTRAINT <br/>notice])

  style A fill:#0060B0,stroke:#002831,color:#FFFFFF
  style B fill:#36C1C4,stroke:#007A76,color:#FFFFFF
  style C fill:#1A1A2E,stroke:#0F0F1A,color:#FFFFFF
  style D fill:#0060B0,stroke:#002831,color:#FFFFFF
  style E fill:#6B7280,stroke:#4B5563,color:#FFFFFF

使用可能なカテゴリー名 (category1category2) は任意のバケット名であり、その意味はマッピングする違反によって定義されます。たとえば、通行止めをcategory1にマッピングして許容される違反とすることができます。これは、ドライバーがルートの最後の区間を徒歩で移動できるためです。同様に、高さ制限をcategory2にマッピングして許容できない違反とし、それらの停車地を未割り当てのままに保ちつつ、どの停車地にそのリスクがあるかを追跡することができます。

category1およびcategory2に加え、以下のignoreRouteViolationsの値は、violationMappingと組み合わせて使用することでさまざまなユースケースに対応します。

動作
category1category1にマッピングされた違反を無視します。category1に属する停車地には、unreachableLocation通知が割り当てられます。
category2category2にマッピングされた違反を無視します。category2に属する停車地には、unreachableLocation通知が割り当てられます。
allExceptCategory1category1にマッピングされた違反以外のすべての違反を無視します。category1の違反によってブロックされた停車地は、割り当てられないままとなります。
allExceptCategory2category2にマッピングされた違反以外のすべての違反を無視します。category2の違反によってブロックされた停車地は、割り当てられないままとなります。
allExceptCategory1AndCategory2category1またはcategory2にマッピングされた違反以外のすべての違反を無視します。いずれかのカテゴリーによってブロックされた停車地は、割り当てられないままとなります。

allExcept*の値は、ルーティング違反に関係なくほとんどの停車地に割り当てを行いたいが、特定のカテゴリーについては明示的に割り当てを行わないようにしたい場合に役立ちます。たとえば、高さ制限のある停車地に関する厳格な安全規則を維持しつつ、その他のすべての違反タイプを受け入れる場合などが該当します。

📘

次の制約に注意してください。

  • violationMapping配列には、categorytypeの一意の組み合わせを10通りまで含めることができます。
  • 配列内の各項目は、正確に1つのtypeと1つのcategoryを参照する必要があります。
  • 同じカテゴリーを複数の項目で使用することはできますが、同じカテゴリー内で同じtypeを重複して指定することはできません。ただし、そのタイプが異なるサブフィールドを参照している場合は除きます。
  • violationMappingを、ignoreRouteViolations: ["all"]または非推奨のignoreRouteViolations: ["traffic"]を組み合わせることはできません。組み合わせるとエラーが返されます。

詳細については、「APIリファレンス」を参照してください。

どの違反をキャプチャするかを制御する

一部の違反タイプでは、制限の特定のプロパティに基づいて、マッピング項目がキャプチャする違反をより細かく選択できるサブフィールドを使用します。使用可能なサブフィールドは違反タイプによって異なります。

  • restrictionheightgrossWeightcurrentWeightbridgetimeDependent
  • trafficbridge
  • avoidAreasAndSegments:サブフィールドなし

各サブフィールドは、"include" (プロパティが適用される必要がある)、"exclude" (プロパティが適用されてはならない)、"ignore" (プロパティがチェックされていない) の3つの値のいずれかを受け付けます。

たとえば、高さ制限のある道路を経由してしか到達できない停止地点に、背の高いトラックが接近している場合を考えてみましょう。次の表は、それぞれの値の挙動とデフォルトを示しています。

意味トラックの例の結果デフォルト対象
"include"このプロパティが制限に適用される場合にのみ違反をマッピングする高さ制限が適用される → 違反がマッピングされる
"exclude"このプロパティが制限に適用される場合、マッピングをブロックする高さ制限が適用される → 違反がマッピングされない → ジョブが割り当てられない (REACHABLE_CONSTRAINT)heightgrossWeightcurrentWeight
"ignore"このプロパティはチェックされない。制限が適用されていてもマッピングは行われる高さ制限が適用されるが、高さはチェックされない → 違反がマッピングされるbridgetimeDependent

違反マッピングの仕組みに関する詳細については、「HERE Matrix Routing API v8の違反マッピング」を参照してください。

違反マッピングに関連するユースケースの例

以下の例では、各違反タイプを難易度順に解説しています。シンプルな設定から始め、徐々により高度な設定へとステップアップしていく構成になっています。

交通違反をマッピングする

この例では、基本となるviolationMappingパターンを直接適用しています。このパターンでは、すべての交通違反がcategory1にマッピングされ、最適化アルゴリズムはこれを無視するよう指示されます。このシナリオでは、配送トラックが、一部の道路がリアルタイムの通行止めの影響を受けている地域内の3か所の停車地へ配送する必要があります。運行管理オペレーターは、通行止めの有無にかかわらずすべての停車地が確実に割り当てられるようにしたいと考えており、どの停車地に交通関連のルーティングリスクがあるかを把握する必要があります。

次の図は、倉庫とジョブの場所、およびルート違反を引き起こす可能性のある道路閉鎖を示しています。

道路閉鎖の影響を受けるジョブの場所のエリア
⚠️

警告

交通状況は絶えず変化します。リアルタイムの道路閉鎖データは時間や場所によって異なるため、交通状況に基づく違反マッピングの例によって生成されるソリューションは、記録された出力とは異なる場合があります。提供された問題のJSONを参照用入力として使用し、この記事内のソリューションJSONは再現可能なものではなく、あくまで説明用として扱ってください。

そのファイルでは、以下のスニペットが示すように、delivery_truckプロファイルはcategory1に属するすべての交通関連違反を無視するように設定されています。

"profiles": [
  {
    "name": "delivery_truck",
    "type": "truck",
    "ignoreRouteViolations": ["category1"],
    "violationMapping": [
      {
        "category": "category1",
        "type": "traffic"
      }
    ]
  }
]

この設定に期待される結果として、交通関連のすべての違反が最適化アルゴリズムによって無視されます。これにより、最終的にすべてのジョブが旅程に割り当てられるようになります。次のセクションには、問題のファイル全体が含まれています。

Click to expand/collapse the sample JSON
{
  "configuration": { "experimentalFeatures": ["violationMapping"] },
  "fleet": {
    "types": [
      {
        "profile": "delivery_truck",
        "amount": 1,
        "capacity": [30],
        "costs": { "fixed": 10, "time": 0.002, "distance": 0.001 },
        "id": "vehicle_1",
        "shifts": [
          {
            "start": {
              "time": "2026-05-15T08:30:00Z",
              "location": { "lat": 52.418586957426044, "lng": 13.205656324722792 }
            }
          }
        ]
      }
    ],
    "profiles": [
      {
        "name": "delivery_truck",
        "type": "truck",
        "ignoreRouteViolations": ["category1"],
        "violationMapping": [
          {
            "category": "category1",
            "type": "traffic"
          }
        ]
      }
    ]
  },
  "plan": {
    "jobs": [
      {
        "id": "Job_1",
        "tasks": {
          "deliveries": [
            {
              "places": [
                {
                  "duration": 300,
                  "location": { "lat": 52.42190948288694, "lng": 13.19405542552992}
                }
              ],
              "demand": [1]
            }
          ]
        }
      },
      {
        "id": "Job_2",
        "tasks": {
          "deliveries": [
            {
              "places": [
                {
                  "duration": 300,
                  "location": { "lat": 52.421612477457856, "lng": 13.20251576168617 }
                }
              ],
              "demand": [1]
            }
          ]
        }
      },
      {
        "id": "Job_3",
        "tasks": {
          "deliveries": [
            {
              "places": [
                {
                  "duration": 300,
                  "location": { "lat": 52.422268864749725, "lng": 13.189670501127011 }
                }
              ],
              "demand": [1]
            }
          ]
        }
      }
    ]
  }
}

ソリューションの解釈 - 交通の例

結果として得られたソリューションでは、期待どおり、最適化アルゴリズムによってすべてのジョブが割り当てられており、ルーティング上のリスクを伴うすべてのジョブ (Job_1Job_3) には、違反タイプ (category1) が強調表示された関連する警告メッセージが付いています。例:

"notices": [
  {
    "code": "unreachableLocation",
    "title": "Due to ignored route violations (category1), this location can be unreachable",
    "action": "Remove ignoreRouteViolations to be sure that location is reachable"
  }
]

詳細については、完全なソリューションのJSONを参照してください。

Click to expand/collapse the sample JSON
{
  "statistic": {
    "cost": 29.093,
    "distance": 14971,
    "duration": 2061,
    "times": {
      "driving": 1161,
      "serving": 900,
      "waiting": 0,
      "stopping": 0,
      "break": 0,
      "intraStop": 0
    },
    "intraStopDistance": 0
  },
  "tours": [
    {
      "vehicleId": "vehicle_1_1",
      "typeId": "vehicle_1",
      "stops": [
        {
          "time": {
            "arrival": "2026-05-15T08:30:00Z",
            "departure": "2026-05-15T08:30:00Z"
          },
          "load": [
            3
          ],
          "activities": [
            {
              "jobId": "departure",
              "type": "departure",
              "location": {
                "lat": 52.41858695742605,
                "lng": 13.205656324722792
              },
              "time": {
                "start": "2026-05-15T08:30:00Z",
                "end": "2026-05-15T08:30:00Z",
                "arrival": "2026-05-15T08:30:00Z"
              }
            }
          ],
          "location": {
            "lat": 52.41858695742605,
            "lng": 13.205656324722792
          },
          "distance": 0
        },
        {
          "time": {
            "arrival": "2026-05-15T08:32:35Z",
            "departure": "2026-05-15T08:37:35Z"
          },
          "load": [
            2
          ],
          "activities": [
            {
              "jobId": "Job_2",
              "type": "delivery",
              "location": {
                "lat": 52.421612477457856,
                "lng": 13.20251576168617
              },
              "time": {
                "start": "2026-05-15T08:32:35Z",
                "end": "2026-05-15T08:37:35Z",
                "arrival": "2026-05-15T08:32:35Z"
              }
            }
          ],
          "location": {
            "lat": 52.421612477457856,
            "lng": 13.20251576168617
          },
          "distance": 582
        },
        {
          "time": {
            "arrival": "2026-05-15T08:52:34Z",
            "departure": "2026-05-15T08:57:34Z"
          },
          "load": [
            1
          ],
          "activities": [
            {
              "jobId": "Job_1",
              "type": "delivery",
              "location": {
                "lat": 52.42190948288694,
                "lng": 13.19405542552992
              },
              "time": {
                "start": "2026-05-15T08:52:34Z",
                "end": "2026-05-15T08:57:34Z",
                "arrival": "2026-05-15T08:52:34Z"
              },
              "notices": [
                {
                  "code": "unreachableLocation",
                  "title": "Due to ignored route violations (category1), this location can be unreachable",
                  "action": "Remove ignoreRouteViolations to be sure that location is reachable"
                }
              ]
            }
          ],
          "location": {
            "lat": 52.42190948288694,
            "lng": 13.19405542552992
          },
          "distance": 14113
        },
        {
          "time": {
            "arrival": "2026-05-15T08:59:21Z",
            "departure": "2026-05-15T09:04:21Z"
          },
          "load": [
            0
          ],
          "activities": [
            {
              "jobId": "Job_3",
              "type": "delivery",
              "location": {
                "lat": 52.422268864749725,
                "lng": 13.189670501127011
              },
              "time": {
                "start": "2026-05-15T08:59:21Z",
                "end": "2026-05-15T09:04:21Z",
                "arrival": "2026-05-15T08:59:21Z"
              },
              "notices": [
                {
                  "code": "unreachableLocation",
                  "title": "Due to ignored route violations (category1), this location can be unreachable",
                  "action": "Remove ignoreRouteViolations to be sure that location is reachable"
                }
              ]
            }
          ],
          "location": {
            "lat": 52.422268864749725,
            "lng": 13.189670501127011
          },
          "distance": 14971
        }
      ],
      "statistic": {
        "cost": 29.093,
        "distance": 14971,
        "duration": 2061,
        "times": {
          "driving": 1161,
          "serving": 900,
          "waiting": 0,
          "stopping": 0,
          "break": 0,
          "intraStop": 0
        },
        "intraStopDistance": 0
      },
      "shiftIndex": 0
    }
  ]
}

回避エリア違反をマッピングする

この例では、同じパターンを別の違反タイプ (avoidAreasAndSegments) に適用します。このシナリオでは、全車両が配送制限区域や混雑地区を表すバウンディングボックスの回避エリアを定義しています (プロファイル内のavoid.areasオブジェクトには、バウンディングボックス、ポリゴン、エンコードされたポリゴン、またはフィーチャーを含めることができます)。一部のジョブの位置がこのゾーン内にあるため、アルゴリズムは、回避エリアを通過させずに車両をそこへ誘導することができません。ジョブの場所および回避すべきバウンディングボックスの視覚的な表現については、次の図を参照してください。

回避バウンディングボックス内のジョブの場所を示す配送エリア

次のスニペットは、avoid.areasを含むdelivery_truckプロファイル設定と、対応するviolationMapping設定を示しています。

"profiles": [
  {
    "name": "delivery_truck",
    "type": "truck",
    "avoid": {
      "areas": [
        {
          "type": "boundingBox",
          "north": 52.508064,
          "south": 52.50677,
          "east": 13.394407,
          "west": 13.392137
        }
      ]
    },
    "ignoreRouteViolations": ["category1"],
    "violationMapping": [
      {
        "category": "category1",
        "type": "avoidAreasAndSegments"
      }
    ]
  }
]

次のセクションでは問題のJSONをすべて示します。期待される動作として、最適化アルゴリズムはバウンディングボックスの違反を無視し、すべてのジョブを旅程に割り当てます。

Click to expand/collapse the sample JSON
{
  "configuration": { "experimentalFeatures": ["violationMapping"] },
  "fleet": {
    "types": [
      {
        "profile": "delivery_truck",
        "amount": 1,
        "capacity": [30],
        "costs": { "fixed": 10, "time": 0.002, "distance": 0.001 },
        "id": "vehicle_1",
        "shifts": [
          {
            "start": {
              "time": "2026-05-15T08:30:00Z",
              "location": { "lat": 52.5062838773854, "lng": 13.386458504898345 }
            }
          }
        ]
      }
    ],
    "profiles": [
      {
        "name": "delivery_truck",
        "type": "truck",
        "avoid": {
          "areas": [
            {
              "type": "boundingBox",
              "north": 52.508064,
              "south": 52.50677,
              "east": 13.394407,
              "west": 13.392137
            }
          ]
        },
        "ignoreRouteViolations": ["category1"],
        "violationMapping": [
          {
            "category": "category1",
            "type": "avoidAreasAndSegments"
          }
        ]
      }
    ]
  },
  "plan": {
    "jobs": [
      {
        "id": "Job_1",
        "tasks": {
          "deliveries": [
            {
              "places": [
                {
                  "duration": 300,
                  "location": { "lat": 52.505941151875504, "lng": 13.392387263950676}
                }
              ],
              "demand": [1]
            }
          ]
        }
      },
      {
        "id": "Job_2_bbox",
        "tasks": {
          "deliveries": [
            {
              "places": [
                {
                  "duration": 300,
                  "location": { "lat": 52.50773539109499, "lng": 13.3935796400729 }
                }
              ],
              "demand": [1]
            }
          ]
        }
      },
      {
        "id": "Job_3_bbox",
        "tasks": {
          "deliveries": [
            {
              "places": [
                {
                  "duration": 300,
                  "location": { "lat": 52.50706003998471, "lng": 13.393215302924446 }
                }
              ],
              "demand": [1]
            }
          ]
        }
      },
      {
        "id": "Job_4",
        "tasks": {
          "deliveries": [
            {
              "places": [
                {
                  "duration": 300,
                  "location": { "lat": 52.5084913688343, "lng": 13.391492981859013}
                }
              ],
              "demand": [1]
            }
          ]
        }
      },
      {
        "id": "Job_5_bbox",
        "tasks": {
          "deliveries": [
            {
              "places": [
                {
                  "duration": 300,
                  "location": { "lat": 52.5075337948351, "lng": 13.39256943252489 }
                }
              ],
              "demand": [1]
            }
          ]
        }
      }
    ]
  }
}

ソリューションの解釈 - 回避エリアの例

次のソリューションに示すように、最適化アルゴリズムはすべてのジョブを旅程に割り当てます。ルーティング上のリスクを伴うすべてのジョブには、制限違反に関する対応する注意書きが含まれています。

次の図は、ジョブが回避バウンディングボックス内に存在するにもかかわらず、最適化アルゴリズムがジョブの場所に車両をルーティングしたことを示しています。

バウンディングボックス内のジョブに対して回避制限が無視された旅程

ソリューションのJSON全体については、次のセクションを参照してください。

Click to expand/collapse the solution JSON
{
  "statistic": {
    "cost": 16.364,
    "distance": 2256,
    "duration": 2054,
    "times": {
      "driving": 554,
      "serving": 1500,
      "waiting": 0,
      "stopping": 0,
      "break": 0,
      "intraStop": 0
    },
    "intraStopDistance": 0
  },
  "tours": [
    {
      "vehicleId": "vehicle_1_1",
      "typeId": "vehicle_1",
      "stops": [
        {
          "time": {
            "arrival": "2026-05-15T08:30:00Z",
            "departure": "2026-05-15T08:30:00Z"
          },
          "load": [
            5
          ],
          "activities": [
            {
              "jobId": "departure",
              "type": "departure",
              "location": {
                "lat": 52.5062838773854,
                "lng": 13.386458504898345
              },
              "time": {
                "start": "2026-05-15T08:30:00Z",
                "end": "2026-05-15T08:30:00Z",
                "arrival": "2026-05-15T08:30:00Z"
              }
            }
          ],
          "location": {
            "lat": 52.5062838773854,
            "lng": 13.386458504898345
          },
          "distance": 0
        },
        {
          "time": {
            "arrival": "2026-05-15T08:31:54Z",
            "departure": "2026-05-15T08:36:54Z"
          },
          "load": [
            4
          ],
          "activities": [
            {
              "jobId": "Job_5_bbox",
              "type": "delivery",
              "location": {
                "lat": 52.5075337948351,
                "lng": 13.39256943252489
              },
              "time": {
                "start": "2026-05-15T08:31:54Z",
                "end": "2026-05-15T08:36:54Z",
                "arrival": "2026-05-15T08:31:54Z"
              },
              "notices": [
                {
                  "code": "unreachableLocation",
                  "title": "Due to ignored route violations (category1), this location can be unreachable",
                  "action": "Remove ignoreRouteViolations to be sure that location is reachable"
                }
              ]
            }
          ],
          "location": {
            "lat": 52.5075337948351,
            "lng": 13.39256943252489
          },
          "distance": 534
        },
        {
          "time": {
            "arrival": "2026-05-15T08:37:17Z",
            "departure": "2026-05-15T08:42:17Z"
          },
          "load": [
            3
          ],
          "activities": [
            {
              "jobId": "Job_2_bbox",
              "type": "delivery",
              "location": {
                "lat": 52.50773539109499,
                "lng": 13.3935796400729
              },
              "time": {
                "start": "2026-05-15T08:37:17Z",
                "end": "2026-05-15T08:42:17Z",
                "arrival": "2026-05-15T08:37:17Z"
              },
              "notices": [
                {
                  "code": "unreachableLocation",
                  "title": "Due to ignored route violations (category1), this location can be unreachable",
                  "action": "Remove ignoreRouteViolations to be sure that location is reachable"
                }
              ]
            }
          ],
          "location": {
            "lat": 52.50773539109499,
            "lng": 13.3935796400729
          },
          "distance": 656
        },
        {
          "time": {
            "arrival": "2026-05-15T08:43:09Z",
            "departure": "2026-05-15T08:48:09Z"
          },
          "load": [
            2
          ],
          "activities": [
            {
              "jobId": "Job_3_bbox",
              "type": "delivery",
              "location": {
                "lat": 52.50706003998471,
                "lng": 13.393215302924446
              },
              "time": {
                "start": "2026-05-15T08:43:09Z",
                "end": "2026-05-15T08:48:09Z",
                "arrival": "2026-05-15T08:43:09Z"
              },
              "notices": [
                {
                  "code": "unreachableLocation",
                  "title": "Due to ignored route violations (category1), this location can be unreachable",
                  "action": "Remove ignoreRouteViolations to be sure that location is reachable"
                }
              ]
            }
          ],
          "location": {
            "lat": 52.50706003998471,
            "lng": 13.393215302924446
          },
          "distance": 884
        },
        {
          "time": {
            "arrival": "2026-05-15T08:49:49Z",
            "departure": "2026-05-15T08:54:49Z"
          },
          "load": [
            1
          ],
          "activities": [
            {
              "jobId": "Job_4",
              "type": "delivery",
              "location": {
                "lat": 52.5084913688343,
                "lng": 13.391492981859011
              },
              "time": {
                "start": "2026-05-15T08:49:49Z",
                "end": "2026-05-15T08:54:49Z",
                "arrival": "2026-05-15T08:49:49Z"
              }
            }
          ],
          "location": {
            "lat": 52.5084913688343,
            "lng": 13.391492981859011
          },
          "distance": 1367
        },
        {
          "time": {
            "arrival": "2026-05-15T08:59:14Z",
            "departure": "2026-05-15T09:04:14Z"
          },
          "load": [
            0
          ],
          "activities": [
            {
              "jobId": "Job_1",
              "type": "delivery",
              "location": {
                "lat": 52.505941151875504,
                "lng": 13.392387263950676
              },
              "time": {
                "start": "2026-05-15T08:59:14Z",
                "end": "2026-05-15T09:04:14Z",
                "arrival": "2026-05-15T08:59:14Z"
              }
            }
          ],
          "location": {
            "lat": 52.505941151875504,
            "lng": 13.392387263950676
          },
          "distance": 2256
        }
      ],
      "statistic": {
        "cost": 16.364,
        "distance": 2256,
        "duration": 2054,
        "times": {
          "driving": 554,
          "serving": 1500,
          "waiting": 0,
          "stopping": 0,
          "break": 0,
          "intraStop": 0
        },
        "intraStopDistance": 0
      },
      "shiftIndex": 0
    }
  ]
}

車両制限違反をマッピングする

この例では、restrictionの違反タイプを使用して、サブフィールドがどの違反をキャプチャするかにどのように影響するか、また2つの規制タイプをどのように別々のカテゴリーにマッピングできるかを示しています。

📘

複数の制限タイプのマッピング:includeignore

1つの項目で複数のサブフィールドをincludeに設定するには、それらのすべての条件が制限に同時に存在する必要があります。たとえば、grossWeightcurrentWeightを考えてみましょう。実際には、grossWeightcurrentWeightの両方の条件を同時に含む制限は存在しないため、これら両方を1つの項目でincludeに設定しても、マッピングは決して行われません。

代わりにignoreを使用すると、次の表に示すとおり、指定されたプロパティのいずれかが該当する場合に違反をマッピングできます。

目標設定
grossWeight違反のみをマッピングする"grossWeight": "include"
currentWeight違反のみをマッピングする"currentWeight": "include"
grossWeightまたはcurrentWeight違反のいずれかをマッピングする"grossWeight": "ignore", "currentWeight": "ignore"
いずれかの違反をマッピングするが、橋の上でのみ適用する"grossWeight": "ignore", "currentWeight": "ignore", "bridge": "include"

このサンプルシナリオでは、高くて重い配送トラック (height: 800 cm、grossWeight: 8000 kg) の全車両が、高さ制限と重量制限の両方が設けられた道路がある地域内の停車地に配送しなければなりません。運行管理オペレーターは、制限に関係なくすべての停車地を割り当てたいと考えていますが、どの停車地がどの種類の違反の影響を受けたかを特定できるソリューションを必要としています。

そのため、プロファイルでは各制限の種類を次に示す別々のカテゴリーにマッピングしています。

  • category1は高さに関する違反 (height: "include")をキャプチャする
  • category2は総重量に関する違反 (grossWeight: "include")をキャプチャする
📘

  • 同じマッピング項目 (たとえばcategory2) にheight: "include"grossWeight: "include"を設定すると、マッピングが適用される条件として、その停車地がheightgrossWeightの両方によってブロックされていなければならなくなります。このサンプルシナリオの文脈では、そのような設定をすると、ジョブが1つも割り当てられないという結果になります。
  • 例で示した設定の代わりに、heightgrossWeightの両方を"ignore"に設定し、それらを単一のマッピングカテゴリーの下に配置することもできます。しかし、その結果ソリューションに出力される通知では、両方が同じカテゴリーに分類されてしまうため、違反タイプの区別がつきません。
  • 1つの停車アクティビティで、複数の違反カテゴリーが記録されることがあります。車両が停車地点に接近する際に異なる制限に遭遇した場合 (たとえば、ある区間では重量制限、別の区間では高さ制限など)、そのアクティビティの通知には、記録された各違反を反映して複数のカテゴリーがリストされることがあります。

両方のカテゴリーがignoreRouteViolationsにリストされているため、すべての停車地が割り当てられることが期待されます。

次の図は、この例のジョブの配分を示し、それらの場所へのルーティングをブロックする可能性のある制限を視覚化したものです。

異なる種類のルーティング制限により2つのジョブがブロックされているサービスエリア

restrictionタイプはtruckプロファイルでのみサポートされます。他のプロファイルタイプで行おうとすると、API検証エラーが返されます。また、各restriction項目でheightgrossWeightcurrentWeightのいずれか少なくとも1つをincludeまたはignoreとして設定する必要があります。これら3つすべてを省略するとエラーが返されます。

次のスニペットは、delivery_truckプロファイルの設定を示しています。

"profiles": [
  {
    "name": "delivery_truck",
    "type": "truck",
    "options": {
      "height": 800,
      "grossWeight": 8000
    },
    "ignoreRouteViolations": ["category1", "category2"],
    "violationMapping": [
      {
        "category": "category1",
        "type": "restriction",
        "height": "include"
      },
      {
        "category": "category2",
        "type": "restriction",
        "grossWeight": "include"
      }
    ]
  }
]

この設定に関する重要なポイントは次のとおりです。

  • options.height: 800options.grossWeight: 8000は、車両の物理的な寸法を定義します。ルーティングエンジンは、これらを道路区間の制限と比較し、違反が発生するかどうかを判断します。
  • 2つのviolationMapping項目は、制限タイプごとに1つずつ別々のカテゴリーを使用します。
  • heightgrossWeightはどちらもデフォルトでexcludeになっているため、明示的にincludeに設定する必要があります。そうしないと、両方の制限タイプでエラーが発生します。

問題JSON全体については、次のセクションを参照してください。

Click to expand/collapse the sample JSON
{
  "configuration": { "experimentalFeatures": ["violationMapping"] },
  "fleet": {
    "types": [
      {
        "profile": "delivery_truck",
        "amount": 2,
        "capacity": [1],
        "costs": { "fixed": 10, "time": 0.002, "distance": 0.001 },
        "id": "vehicle_1",
        "shifts": [
          {
            "start": {
              "time": "2026-05-15T08:30:00Z",
              "location": { "lat": 52.5062838773854, "lng": 13.386458504898345 } 
            }
          }
        ]
      }
    ],
    "profiles": [
      {
        "name": "delivery_truck",
        "type": "truck",
        "options": { 
          "height": 800,
          "grossWeight": 8000 
        },
        "ignoreRouteViolations": ["category1", "category2"],
        "violationMapping": [
          {
            "category": "category1",
            "type": "restriction",
            "height": "include"
          },
          {
            "category": "category2",
            "type": "restriction",
            "grossWeight": "include"
          }
        ]
      }
    ]
  },
  "plan": {
    "jobs": [
      {
        "id": "Job_1_height",
        "tasks": {
          "deliveries": [
            {
              "places": [
                {
                  "duration": 300,
                  "location": { "lat": 52.515983, "lng": 13.398930}
                }
              ],
              "demand": [1]
            }
          ]
        }
      },
      {
        "id": "Job_2_grossWeight",
        "tasks": {
          "deliveries": [
            {
              "places": [
                {
                  "duration": 300,
                  "location": { "lat": 52.50292999778208, "lng": 13.396585347667896}
                }
              ],
              "demand": [1]
            }
          ]
        }
      }
    ]
  }
}

ソリューションの解釈 - 制限の例

すべてのジョブが割り当てられています。制限違反をトリガーした各ジョブには、その違反がどのカテゴリーにマッピングされたかを示すunreachableLocation通知が含まれています。

  • Job_1_heightが高さ制限をトリガーしました。その通知はcategory1を参照しています。
  • Job_2_grossWeightは総重量制限をトリガーしました。その通知はcategory2を参照しています。

次の図は、ソリューションを図示したものです。

2台のトラックからなる運行管理の旅程計画で、対応する違反が割り当てられた各ジョブが表示されている

通知に含まれるカテゴリーラベルにより、ソリューションが実行可能になります。category1の通知は、その停車地点に高さに関するリスクがあることをディスパッチャに伝えます。category2の通知は、重量に関するリスクがあることを伝えます。カテゴリーが個別に設定されていなければ、両方の違反タイプで同一の通知が生成され、その区別ができなくなります。

ignoreRouteViolationsからいずれかのカテゴリーを削除すると、対応する停車地は割り当てられなくなります。たとえばcategory2を削除すると、Job_1_heightは引き続き割り当てられままですが、Job_2_grossWeightは未割り当てのままになります。

ソリューションのJSON全体については、次のセクションを参照してください。

Click to expand/collapse the sample JSON

{
  "statistic": {
    "cost": 29.302999999999997,
    "distance": 5883,
    "duration": 1710,
    "times": {
      "driving": 1110,
      "serving": 600,
      "waiting": 0,
      "stopping": 0,
      "break": 0,
      "intraStop": 0
    },
    "intraStopDistance": 0
  },
  "tours": [
    {
      "vehicleId": "vehicle_1_1",
      "typeId": "vehicle_1",
      "stops": [
        {
          "time": {
            "arrival": "2026-05-15T08:30:00Z",
            "departure": "2026-05-15T08:30:00Z"
          },
          "load": [
            1
          ],
          "activities": [
            {
              "jobId": "departure",
              "type": "departure",
              "location": {
                "lat": 52.5062838773854,
                "lng": 13.386458504898345
              },
              "time": {
                "start": "2026-05-15T08:30:00Z",
                "end": "2026-05-15T08:30:00Z",
                "arrival": "2026-05-15T08:30:00Z"
              }
            }
          ],
          "location": {
            "lat": 52.5062838773854,
            "lng": 13.386458504898345
          },
          "distance": 0
        },
        {
          "time": {
            "arrival": "2026-05-15T08:36:14Z",
            "departure": "2026-05-15T08:41:14Z"
          },
          "load": [
            0
          ],
          "activities": [
            {
              "jobId": "Job_2_grossWeight",
              "type": "delivery",
              "location": {
                "lat": 52.50292999778208,
                "lng": 13.396585347667896
              },
              "time": {
                "start": "2026-05-15T08:36:14Z",
                "end": "2026-05-15T08:41:14Z",
                "arrival": "2026-05-15T08:36:14Z"
              },
              "notices": [
                {
                  "code": "unreachableLocation",
                  "title": "Due to ignored route violations (category2), this location can be unreachable",
                  "action": "Remove ignoreRouteViolations to be sure that location is reachable"
                }
              ]
            }
          ],
          "location": {
            "lat": 52.50292999778208,
            "lng": 13.396585347667896
          },
          "distance": 1936
        }
      ],
      "statistic": {
        "cost": 13.284,
        "distance": 1936,
        "duration": 674,
        "times": {
          "driving": 374,
          "serving": 300,
          "waiting": 0,
          "stopping": 0,
          "break": 0,
          "intraStop": 0
        },
        "intraStopDistance": 0
      },
      "shiftIndex": 0
    },
    {
      "vehicleId": "vehicle_1_2",
      "typeId": "vehicle_1",
      "stops": [
        {
          "time": {
            "arrival": "2026-05-15T08:30:00Z",
            "departure": "2026-05-15T08:30:00Z"
          },
          "load": [
            1
          ],
          "activities": [
            {
              "jobId": "departure",
              "type": "departure",
              "location": {
                "lat": 52.5062838773854,
                "lng": 13.386458504898345
              },
              "time": {
                "start": "2026-05-15T08:30:00Z",
                "end": "2026-05-15T08:30:00Z",
                "arrival": "2026-05-15T08:30:00Z"
              }
            }
          ],
          "location": {
            "lat": 52.5062838773854,
            "lng": 13.386458504898345
          },
          "distance": 0
        },
        {
          "time": {
            "arrival": "2026-05-15T08:42:16Z",
            "departure": "2026-05-15T08:47:16Z"
          },
          "load": [
            0
          ],
          "activities": [
            {
              "jobId": "Job_1_height",
              "type": "delivery",
              "location": {
                "lat": 52.515983,
                "lng": 13.39893
              },
              "time": {
                "start": "2026-05-15T08:42:16Z",
                "end": "2026-05-15T08:47:16Z",
                "arrival": "2026-05-15T08:42:16Z"
              },
              "notices": [
                {
                  "code": "unreachableLocation",
                  "title": "Due to ignored route violations (category1), this location can be unreachable",
                  "action": "Remove ignoreRouteViolations to be sure that location is reachable"
                }
              ]
            }
          ],
          "location": {
            "lat": 52.515983,
            "lng": 13.39893
          },
          "distance": 3947
        }
      ],
      "statistic": {
        "cost": 16.019,
        "distance": 3947,
        "duration": 1036,
        "times": {
          "driving": 736,
          "serving": 300,
          "waiting": 0,
          "stopping": 0,
          "break": 0,
          "intraStop": 0
        },
        "intraStopDistance": 0
      },
      "shiftIndex": 0
    }
  ]
}

複数のカテゴリーを使用し、違反タイプを組み合わせる

1つのカテゴリーには、異なるタイプの違反マッピング項目を複数含めることができます。これにより、これらすべての違反タイプに対する運用上の対応が同一であれば、それらをグループ化することで、単一のignoreRouteViolations値ですべてを網羅できるようになります。

このシナリオでは、高さと重量の大きいトラック (height: 800 cm、grossWeight: 8000 kg) の全車両が、都市部と郊外が混在する地域で5つのジョブをこなしています。関連する車両プロファイルには、以下の違反マッピング設定が含まれています。

  • 3種類の違反 (通行止め、回避エリアの通過、総重量制限) は運用上許容され、category1にグループ化されている
  • 高さ制限は厳格な安全基準であり、category2に分類される
  • ignoreRouteViolations: ["allExceptCategory2"]はポリシー全体を網羅する単一の値

次のスニペットは、マッピング違反の要件をキャプチャします。

"violationMapping": [
    { "category": "category1", "type": "traffic" },
    { "category": "category1", "type": "avoidAreasAndSegments" },
    { "category": "category1", "type": "restriction", "grossWeight": "include" },
    { "category": "category2", "type": "restriction", "height": "include" }
  ],
"ignoreRouteViolations": ["allExceptCategory2"]

この設定により、次のような実用上の影響が生じます。

  • 3つの違反タイプがcategory1を共有している
  • allExceptCategory2オプションは、category2にマッピングされていないものをすべて無視するため、無視すべきすべての違反タイプを単一の値でカバーする
  • category2に含まれるタイプは高さのみ
  • allExceptCategory2category2を無視しないため、高さ制限により通行不能となった停車地は未割り当てのままとなる

旅程計画は以下のジョブで構成されています。

  • Job_1_heightJob_4_height:高さ制限のある地下道を通過する必要がある
  • Job_2_grossWeigth:重量制限のある道路を通過する必要がある
  • Job_3_traffic:リアルタイムの通行止めにより通行不能となっている
  • Job_5_bbox:回避バウンディングボックスの範囲内に位置している

次のセクションでは、問題設定の全体について説明します。

Click to expand/collapse the sample JSON
{
  "configuration": { "experimentalFeatures": ["violationMapping"] },
  "fleet": {
    "types": [
      {
        "profile": "delivery_truck",
        "amount": 1,
        "capacity": [30],
        "costs": { "fixed": 10, "time": 0.002, "distance": 0.001 },
        "id": "vehicle_1",
        "shifts": [
          {
            "start": {
              "time": "2026-05-15T08:30:00Z",
              "location": { "lat": 52.5062838773854, "lng": 13.386458504898345 }
            }
          }
        ]
      }
    ],
    "profiles": [
      {
        "name": "delivery_truck",
        "type": "truck",
        "options": { 
          "height": 800,
          "grossWeight": 8000 
        },
        "avoid": {
          "areas": [
            {
              "type": "boundingBox",
              "north": 52.463258,
              "south": 52.461414,
              "east": 13.313586,
              "west": 13.309732
            }
          ]
        },
        "violationMapping": [
          { "category": "category1", "type": "traffic" },
          { "category": "category1", "type": "avoidAreasAndSegments" },
          { "category": "category1", "type": "restriction", "grossWeight": "include" },
          { "category": "category2", "type": "restriction", "height": "include" }
        ],
        "ignoreRouteViolations": ["allExceptCategory2"]
      }
    ]
  },
  "plan": {
    "jobs": [
      {
        "id": "Job_1_height",
        "tasks": {
          "deliveries": [
            {
              "places": [
                {
                  "duration": 300,
                  "location": { "lat": 52.443484936711805, "lng": 13.348484551197322 }
                }
              ],
              "demand": [1]
            }
          ]
        }
      },
      {
        "id": "Job_2_grossWeigth",
        "tasks": {
          "deliveries": [
            {
              "places": [
                {
                  "duration": 300,
                  "location": { "lat": 52.5028909960123, "lng": 13.397018480136607 }
                }
              ],
              "demand": [1]
            }
          ]
        }
      },
      {
        "id": "Job_3_traffic",
        "tasks": {
          "deliveries": [
            {
              "places": [
                {
                  "duration": 300,
                  "location": { "lat": 52.49173924851315, "lng": 13.401725806085613 }
                }
              ],
              "demand": [1]
            }
          ]
        }
      },
      {
        "id": "Job_4_height",
        "tasks": {
          "deliveries": [
            {
              "places": [
                {
                  "duration": 300,
                  "location": { "lat": 52.41796351389373, "lng": 13.314499243723935 }
                }
              ],
              "demand": [1]
            }
          ]
        }
      },
      {
        "id": "Job_5_bbox",
        "tasks": {
          "deliveries": [
            {
              "places": [
                {
                  "duration": 300,
                  "location": { "lat": 52.462269714892976, "lng": 13.311738706185261 }
                }
              ],
              "demand": [1]
            }
          ]
        }
      }
    ]
  }
}

ソリューションの解釈 - 複数のカテゴリーの例

前の問題のviolationMappingignoreRouteViolationsの設定に基づいて、最適化アルゴリズムは次のような旅程を作成しました。

  • Job_2_grossWeigth:割り当て済み、通知はcategory1を参照 (総重量、明示的にマッピング済み)
  • Job_3_traffic:割り当て済み、通知はcategory1を参照 (通行止め、明示的にマッピング済み)
  • Job_5_bbox:割り当て済み、通知はcategory1を参照 (回避エリア、明示的にマッピング済み)
  • Job_1_heightおよびJob_4_heightREACHABLE_CONSTRAINTにより未割り当て (高さはcategory2にマッピングされており、allExceptCategory2ではこれを無視しない)

次の図は、最適化前のジョブと制限の配置、およびその結果として得られた旅程計画を示しています。

問題ソリューション
最適化前のジョブの場所とルーティングの制約複数の違反区分を適用した後の旅程計画
📘

allExceptCategory2は、どのマッピング項目にもキャプチャされなかった違反も無視します。これらは通知ではallではなくcategory1として表示されます。したがって、category1のラベルは、意図的かつ追跡可能なマッピングが正しく行われていることの明確な確認となります。allという値は、その停車地が明示的なマッピング設定の範囲外で、広範なフォールバックによって割り当てられたことを意味します。

ソリューションの全体を見るには、次のセクションを展開してください。

Click to expand/collapse the sample JSON
{
  "statistic": {
    "cost": 32.901,
    "distance": 16227,
    "duration": 3337,
    "times": {
      "driving": 2437,
      "serving": 900,
      "waiting": 0,
      "stopping": 0,
      "break": 0,
      "intraStop": 0
    },
    "intraStopDistance": 0
  },
  "tours": [
    {
      "vehicleId": "vehicle_1_1",
      "typeId": "vehicle_1",
      "stops": [
        {
          "time": {
            "arrival": "2026-05-15T08:30:00Z",
            "departure": "2026-05-15T08:30:00Z"
          },
          "load": [
            3
          ],
          "activities": [
            {
              "jobId": "departure",
              "type": "departure",
              "location": {
                "lat": 52.5062838773854,
                "lng": 13.386458504898345
              },
              "time": {
                "start": "2026-05-15T08:30:00Z",
                "end": "2026-05-15T08:30:00Z",
                "arrival": "2026-05-15T08:30:00Z"
              }
            }
          ],
          "location": {
            "lat": 52.5062838773854,
            "lng": 13.386458504898345
          },
          "distance": 0
        },
        {
          "time": {
            "arrival": "2026-05-15T08:35:43Z",
            "departure": "2026-05-15T08:40:43Z"
          },
          "load": [
            2
          ],
          "activities": [
            {
              "jobId": "Job_2_grossWeigth",
              "type": "delivery",
              "location": {
                "lat": 52.5028909960123,
                "lng": 13.397018480136609
              },
              "time": {
                "start": "2026-05-15T08:35:43Z",
                "end": "2026-05-15T08:40:43Z",
                "arrival": "2026-05-15T08:35:43Z"
              },
              "notices": [
                {
                  "code": "unreachableLocation",
                  "title": "Due to ignored route violations (category1), this location can be unreachable",
                  "action": "Remove ignoreRouteViolations to be sure that location is reachable"
                }
              ]
            }
          ],
          "location": {
            "lat": 52.5028909960123,
            "lng": 13.397018480136609
          },
          "distance": 1705
        },
        {
          "time": {
            "arrival": "2026-05-15T08:55:28Z",
            "departure": "2026-05-15T09:00:28Z"
          },
          "load": [
            1
          ],
          "activities": [
            {
              "jobId": "Job_3_traffic",
              "type": "delivery",
              "location": {
                "lat": 52.49173924851315,
                "lng": 13.401725806085611
              },
              "time": {
                "start": "2026-05-15T08:55:28Z",
                "end": "2026-05-15T09:00:28Z",
                "arrival": "2026-05-15T08:55:28Z"
              },
              "notices": [
                {
                  "code": "unreachableLocation",
                  "title": "Due to ignored route violations (category1), this location can be unreachable",
                  "action": "Remove ignoreRouteViolations to be sure that location is reachable"
                }
              ]
            }
          ],
          "location": {
            "lat": 52.49173924851315,
            "lng": 13.401725806085611
          },
          "distance": 5918
        },
        {
          "time": {
            "arrival": "2026-05-15T09:20:37Z",
            "departure": "2026-05-15T09:25:37Z"
          },
          "load": [
            0
          ],
          "activities": [
            {
              "jobId": "Job_5_bbox",
              "type": "delivery",
              "location": {
                "lat": 52.462269714892976,
                "lng": 13.31173870618526
              },
              "time": {
                "start": "2026-05-15T09:20:37Z",
                "end": "2026-05-15T09:25:37Z",
                "arrival": "2026-05-15T09:20:37Z"
              },
              "notices": [
                {
                  "code": "unreachableLocation",
                  "title": "Due to ignored route violations (category1), this location can be unreachable",
                  "action": "Remove ignoreRouteViolations to be sure that location is reachable"
                }
              ]
            }
          ],
          "location": {
            "lat": 52.462269714892976,
            "lng": 13.31173870618526
          },
          "distance": 16227
        }
      ],
      "statistic": {
        "cost": 32.901,
        "distance": 16227,
        "duration": 3337,
        "times": {
          "driving": 2437,
          "serving": 900,
          "waiting": 0,
          "stopping": 0,
          "break": 0,
          "intraStop": 0
        },
        "intraStopDistance": 0
      },
      "shiftIndex": 0
    }
  ],
  "unassigned": [
    {
      "jobId": "Job_1_height",
      "reasons": [
        {
          "code": "REACHABLE_CONSTRAINT",
          "description": "location unreachable"
        }
      ]
    },
    {
      "jobId": "Job_4_height",
      "reasons": [
        {
          "code": "REACHABLE_CONSTRAINT",
          "description": "location unreachable"
        }
      ]
    }
  ]
}

次のステップ


このページは役に立ちましたか?