Public
Edited
Aug 23, 2023
1 fork
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
companiesToOriginHubDirections = Promise.all(
MANUFACTURERS.map((manufacturer) =>
getDirection(manufacturer.location, ORIGIN_HUB.location)
)
)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
mvrpInputs = {
const locations = [];
const pickupLocationIndex = 0;
locations.push(transformPoint(ORIGIN_HUB.location));

let locationIndex = 1;
const jobs = [];

const hubLocationIndices = {};
for (const hub of DESTINATION_HUBS) {
hubLocationIndices[hub.city] = locationIndex++;
locations.push(transformPoint(hub.location));
}

for (let orderId = 0; orderId < ORDERS.length; orderId++) {
const order = ORDERS[orderId];
const deliveryLocationIndex = hubLocationIndices[order.city];
jobs.push({
id: orderId,
location_index: deliveryLocationIndex,
delivery: [PRODUCTS[order.itemId].weight],
pickup: [PRODUCTS[order.itemId].weight]
});
}

const vehicles = [];
for (const truck of TRUCKS_AT_ORIGIN_HUB) {
vehicles.push({
id: truck.id,
capacity: [truck.capacity],
start_index: 0
// max_tasks: 1
});
}

return {
depots: [{ id: 0, location_index: 0 }],
locations: {
id: 1,
location: locations.join("|")
},
jobs,
vehicles
};
}
Insert cell
mvrpLongHaul = {
const result = await MVRP(mvrpInputs);
return result;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
const parsedGeometry = {
type: "LineString",
coordinates: nextbillion.utils.polyline
.decode(selectedLongHaulRoute.geometry, 5)
.map((c) => c.reverse())
};
const centroid =
parsedGeometry.coordinates[
Math.floor(parsedGeometry.coordinates.length / 2)
];

const nbmap = new nextbillion.maps.Map({
container: document.getElementById("long-haul-mvrp-result"),
center: {
lat: centroid[1],
lng: centroid[0]
},
zoom: 5,
maxZoom: 15,
minZoom: 0,
style: "https://api.nextbillion.io/maps/streets/style.json",
scrollZoom: true
});

nbmap.on("load", () => {
drawRoute(nbmap.map, "route", selectedLongHaulRoute);
function getUniqueCoordinates(coords) {
const uniqueCoords = new Set(coords.map(JSON.stringify));
return Array.from(uniqueCoords).map(JSON.parse);
}
const uniqueSteps = getUniqueCoordinates(
selectedLongHaulRoute.steps.map((s) => s.location)
);
uniqueSteps.forEach((location, idx) => {
drawStepIdx(nbmap.map, location.slice().reverse(), idx);
});
});
}
Insert cell
Insert cell
Insert cell
mvrpFromDestinationHubs = {
async function performMVRP(cityCode) {
const orders = ORDERS.filter((order) => order.city === cityCode);
const locations = [
transformPoint(
DESTINATION_HUBS.find((hub) => hub.city === cityCode).location
)
];
const hubLocationIndex = 0;

let jobId = 0;
const jobs = [];
let locationIndex = 1;

for (let i = 0; i < orders?.length; i++) {
const order = orders[i];
const store = STORES[order.storeId];
jobs.push({
id: jobId++,
delivery: [PRODUCTS[order.itemId].weight],
location_index: locationIndex++,
skills: PRODUCTS[order.itemId].skills
});
locations.push(transformPoint(store.location));
}

let vehicles = [];
for (const vehicle of shortHaulTrucks[cityCode]) {
vehicles.push({
id: vehicle.id,
skills: _.random() ? [] : [0],
start_index: hubLocationIndex,
capacity: [inputs.shortHaulTruckCapacity],
end_index: hubLocationIndex,
description: vehicle.description
});
}

const result = await MVRP({
locations: {
id: 1,
location: locations.join("|")
},
jobs,
vehicles
});
return {
cityCode,
...result
};
}

return Promise.all(
_.map(
DESTINATION_HUBS.map((hub) => hub.city),
performMVRP
)
);
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
const center = CITIES[selectedDestination.cityCode].centroid;

const nbmap = new nextbillion.maps.Map({
container: document.getElementById("destination-map"),
center: {
lat: center[1],
lng: center[0]
},
zoom: 10,
maxZoom: 15,
minZoom: 0,
style: "https://api.nextbillion.io/maps/streets/style.json",
scrollZoom: true
});

nbmap.on("load", () => {
STORES.filter((store) => store.city === selectedDestination.cityCode).map(
(store) => drawStore(nbmap.map, store)
);
drawHub(
nbmap.map,
DESTINATION_HUBS.find((hub) => hub.city === selectedDestination.cityCode)
);
selectedDestinationTrucks.forEach((route, i) => {
drawRoute(nbmap.map, `route-${i}`, route, COLORS[route.vehicle]);
});
});
}
Insert cell
Insert cell
async function getDirection(
origin,
destination,
options = {
mode: "truck",
option: "flexible",
route_type: "shortest",
truck_size: "200,210,600"
}
) {
const baseUrl = "https://api.nextbillion.io/directions/json";
const from = `origin=${transformPoint(origin)}`;
const to = `destination=${transformPoint(destination)}`;
const mode = `mode=${options.mode}`;
const option = `option=${options.option}`;
const truck_size = `truck_size=${option.truck_size}`;
const routeType = `route_type=${options.route_type}`;
const url = `${baseUrl}?${from}&${to}&${mode}&${routeType}&${option}&key=${API_KEY}`;
const result = await fetch(url);
return result.json().then((r) => r.routes);
}
Insert cell
function drawRoute(map, source, data, color = "#7e22ce", width = 5) {
const { distance, duration, geometry } = data;
const parsedGeometry = {
type: "LineString",
coordinates: nextbillion.utils.polyline
.decode(geometry, 5)
.map((c) => c.reverse())
};
const popupLngLat =
parsedGeometry.coordinates[
Math.floor(parsedGeometry.coordinates.length / 2)
];
if (!map.getSource(source)) {
map.addSource(source, {
type: "geojson",
data: {
type: "Feature",
geometry: {
type: "LineString",
coordinates: nextbillion.utils.polyline
.decode(geometry, 5)
.map((c) => c.reverse())
}
}
});

map.addLayer({
id: source,
type: "line",
source,
layout: { "line-join": "round", "line-cap": "round" },
paint: {
"line-color": color,
"line-width": width
// "line-dasharray": [1, 2]
}
});

// const element = document.createElement("div");
// element.innerHTML = `
// <div>distance: ${_.round(distance / 1000, 1)} Km</div>
// <div>duration: ${_.round(duration / 3600, 1)} Hours</div>
// `;
// element.style.fontSize = "12";
// element.style.backgroundColor = color;
// element.style.color = "white";
// element.style.fontWeight = "600";
// new nextbillion.maps.Marker({ element }).setLngLat(popupLngLat).addTo(map);
}
}
Insert cell
function transformPoint(p) {
return [p[1], p[0]].join(",");
}
Insert cell
async function MVRP(data) {
const optimizeRequestUrl =
`https://api.nextbillion.io/optimise-mvrp?` + `key=${API_KEY}`;

const optimizeRequest = await axios
.post(optimizeRequestUrl, {
...data,
options: {
objective: { minimise_num_depots: false, travel_cost: "distance" },
routing: { mode: "truck" }
}
})
.then((res) => res.data);

const mvrpRequestId = optimizeRequest.id;
const previewurl = `https://nb-playground-staging.netlify.app/optimization-tester?apiKey=${API_KEY}&requestID=${mvrpRequestId}`;
const getResultUrl = `https://api.nextbillion.io/optimise-mvrp/result?id=${mvrpRequestId}&key=${API_KEY}`;

// Wait until the Route Optimization result is ready
let attempts = 0;
while (attempts < 10) {
const result = await axios({ url: getResultUrl, method: "GET" });

if (
result.data &&
result.data.status === "Ok" &&
result.data.message == ""
) {
console.log(result);
return result.data;
}
attempts++;
await new Promise((resolve) => setTimeout(resolve, 5 * 1000));
}
}
Insert cell
function drawHub(map, hub) {
const hubLocation = { lat: hub.location[1], lng: hub.location[0] };
const element = document.createElement("div");
element.innerHTML = `<div style='text-align: center;><div style='text-align: center;>🏢<div><div class='display-text'>Hub: ${hub.name}</div></div>`;
element.style.fontSize = "15px";
element.style.color = "black";
element.style.fontWeight = "600";
new nextbillion.maps.Marker({ element }).setLngLat(hubLocation).addTo(map);
}
Insert cell
function drawStepIdx(map, location, stepIdx) {
const stepIdxLocation = { lat: location[1], lng: location[0] };
const element = document.createElement("div");
element.innerText = NUMBER_EMOJIS[stepIdx];
element.style.fontSize = "15px";
new nextbillion.maps.Marker({ element })
.setLngLat(stepIdxLocation)
.addTo(map);
}
Insert cell
function drawManufacturer(map, company) {
const element = document.createElement("div");
element.innerHTML = `<div style='text-align: center;'><div style='text-align: center;'>🏭</div><div class="display-text">Company: ${company.name}</div></div>`;
element.style.color = "black";
element.style.fontWeight = "600";
element.style.fontSize = "15px";
new nextbillion.maps.Marker({ element })
.setLngLat({
lat: company.location[1],
lng: company.location[0]
})
.addTo(map);
}
Insert cell
function drawStore(map, store) {
const element = document.createElement("div");
element.innerHTML = `<div style='text-align: center;'><div style='text-align: center;'>🏬</div><div class='display-text'>Store: ${store.name}</div></div>`;
element.style.color = "black";
element.style.fontWeight = "600";
element.style.fontSize = "15px";
new nextbillion.maps.Marker({ element })
.setLngLat({
lat: store.location[1],
lng: store.location[0]
})
.addTo(map);
}
Insert cell
function* createMap(id, center, zoom = 10, onLoad) {
const container = html`
<div id="${id}" style="height:600px;width:100%;"></div>
`;
yield container;

const nbmap = new nextbillion.maps.Map({
container: document.getElementById(id),
center: {
lat: center[1],
lng: center[0]
},
zoom,
maxZoom: 15,
minZoom: 0,
style: "https://api.nextbillion.io/maps/streets/style.json",
scrollZoom: true
});

nbmap.on("load", () => {
onLoad(nbmap);
});
}
Insert cell
Insert cell
Insert cell
ORIGIN_HUB = {
return {
name: "Columbus Shipping Corp",
city: "columbus",
location: [-82.99876, 39.9869],
postalCode: "43201-2816"
};
}
Insert cell
PRODUCTS = {
return {
ac: {
name: "AC",
weight: 300,
skills: []
},
fridge: {
name: "Frige",
weight: 1000,
skills: [0]
},
dish_washer: {
name: "Dish Washer",
weight: 500,
skills: []
},
washing_machine: {
name: "Washing Machine",
weight: 800,
skills: [0]
}
};
}
Insert cell
MANUFACTURERS = {
return [
{
name: "Best AC Company",
item: "ac",
city: "columbus",
location: [-82.92288, 40.09587],
postalCode: "43081",
id: 0
},
{
name: "Cool Fridges",
item: "fridge",
city: "columbus",
location: [-82.84959, 39.89277],
postalCode: "43110",
id: 1
},
{
name: "Dishy Dish Washers Corp",
item: "dish_washer",
city: "columbus",
location: [-83.1163, 39.90666],
postalCode: "43123",
id: 2
},
{
name: "Wishing Machines",
item: "washing_machine",
city: "columbus",
location: [-83.34013, 40.1381],
postalCode: "43064",
id: 3
}
];
}
Insert cell
DESTINATION_HUBS = {
return [
{
name: "Austin Hub",
city: "austin",
location: [-97.73751, 30.32101],
postalCode: "78756-2625"
},
{
name: "Los Angeles Hub",
city: "la",
location: [-118.24741, 34.04014],
postalCode: "90014-2417"
},
{
name: "Detroit Hub",
city: "detroit",
location: [-83.07939, 42.38504],
postalCode: "48202"
}
];
}
Insert cell
ORDERS = STORES.flatMap((store) => {
const orders = [];
for (const manufacturer of MANUFACTURERS) {
if (_.random()) {
orders.push({
storeId: store.id,
itemId: manufacturer.item,
quantity: _.random(50, 80),
city: store.city
});
}
}
return orders;
})
Insert cell
STORES = {
return [
{
name: "Smitham Inc",
city: "austin",
location: [-97.67775, 30.22351],
postalCode: "78742-2811",
id: 0
},
{
name: "Goyette, Stroman and Schiller",
city: "austin",
location: [-97.75569, 30.25644],
postalCode: "78704-1614",
id: 1
},
{
name: "Feest and Hermann",
city: "austin",
location: [-97.85122, 30.40729],
postalCode: "78726",
id: 2
},
{
name: "Bashirian and Sons",
city: "austin",
location: [-97.77488, 30.53963],
postalCode: "78613-6923",
id: 3
},
{
name: "Jones - Durgan",
city: "la",
location: [-118.12816, 34.09664],
postalCode: "91801-3547",
id: 4
},
{
name: "Larson - Schowalter",
city: "la",
location: [-118.29751, 33.9826],
postalCode: "90044-2731",
id: 5
},
{
name: "Baumbach, Gutkowski and Schinner",
city: "la",
location: [-118.08295, 33.94231],
postalCode: "90670-3643",
id: 6
},
{
name: "Stark Inc",
city: "la",
location: [-118.39707, 33.99065],
postalCode: "90230-5358",
id: 7
},
{
name: "Casper, Bashirian and Bogisich",
city: "detroit",
location: [-83.26847, 42.44302],
postalCode: "48219-1122",
id: 8
},
{
name: "Keeling Inc",
city: "detroit",
location: [-82.91314, 42.38731],
postalCode: "48230-1504",
id: 9
},
{
name: "Mueller - Bosco",
city: "detroit",
location: [-83.23414, 42.34304],
postalCode: "48228-4940",
id: 10
},
{
name: "Rice - MacGyver",
city: "detroit",
location: [-82.96492, 42.4793],
postalCode: "48021-1008",
id: 11
}
];
}
Insert cell
LONG_HAUL_TRUCKS = [
"T680",
"579",
"Cascadia",
"VNL",
"LT",
"Anthem",
"5700XE",
"T880",
"389",
"Coronado",
"VNR",
"LoneStar",
"Pinnacle",
"4900",
"W990"
]
Insert cell
NUMBER_EMOJIS[0]
Insert cell
NUMBER_EMOJIS = ["1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣", "7️⃣", "8️⃣", "9️⃣"]
Insert cell
USA_CENTROID = [-99.0909, 39.8355]
Insert cell
CITIES = {
return {
columbus: {
name: "Columbus",
state: "Ohio",
centroid: [-83.001647, 39.963483]
},
austin: {
name: "Austin",
state: "Texas",
centroid: [-97.7431, 30.2672]
},
la: {
name: "Los Angeles",
state: "California",
centroid: [-118.243683, 34.052235]
},
detroit: {
name: "Detroit",
state: "Michigan",
centroid: [-83.045753, 42.331429]
}
};
}
Insert cell
COLORS = [
"#FF6633",
"#FF33FF",
"#00B3E6",
"#E6B333",
"#3366E6",
"#B34D4D",
"#E666FF",
"#4DB3FF",
"#CC80CC",
"#809900"
]
Insert cell
Insert cell
Insert cell
nextbillion.setApiKey(API_KEY)
Insert cell
faker = require("https://unpkg.com/faker@5.3.1/dist/faker.min.js")
Insert cell
axios = {
let axios = await require("axios");
return axios;
}
Insert cell
import {
guard,
colors,
renderPolygon,
d3Delaunay,
findExtremeCoordinates
} from "@texoslab/nbai-helpers"
Insert cell
import {
nextbillion,
secret2,
styles,
routeStyles
} from "@nbai/nextbillion-ai-dependencies"
Insert cell
d3 = require("d3-selection")
Insert cell
html`<b>Add the stylesheet for the NB.ai SDK </b><br><code>&nbsp;&lt;link href="https://maps-gl.nextbillion.io/maps/v2/api/css" rel="stylesheet"&gt;</code>`
Insert cell
Insert cell
<style>
.custom-popup p {
margin: 0;
padding: 0;
}
.display-text {
background-color: white;
}
.sign {
text-align: center;
}
</style>
Insert cell
geoviewer = require("https://d12qcqjlhp2ahm.cloudfront.net/index_alltrans.min.js").then(
(f) => f.view
)
Insert cell
milestones = require("d3-milestones")
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more