Public
Edited
Sep 7, 2023
1 fork
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
ordersPerStore = {
return {
LOWER_LIMIT: 0,
UPPER_LIMIT: 5
};
}
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 shipments = [];

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: 20
});
}

return {
depots: [{ id: 0, location_index: 0 }],
locations: {
id: 1,
location: locations.join("|")
},
jobs,
// shipments,
vehicles
};
}
Insert cell
mvrpLongHaul = {
const result = await MVRP(mvrpInputs);
return result;
}
Insert cell
Insert cell
Insert cell
longHaulTrucksTable = {
const locationIdsMap = _.fromPairs(
DESTINATION_HUBS.map((h, idx) => [idx + 1, h.city])
);

return longHaulRoutes.map((route) => {
const orders = route.steps
.filter((s) => s.type === "job")
.map((s) => ORDERS[s.id]);
let itemsCount = { fridge: 0, ac: 0, dish_washer: 0, washing_machine: 0 };
for (let order of orders) {
itemsCount[order.itemId] += order.quantity;
}

return {
vehicle: LONG_HAUL_TRUCKS[route.vehicle],
"Destination City":
CITIES[locationIdsMap[route.steps.at(-1).location_index]].name,
duration: _.round(route.duration / 3600, 1),
distance: _.round(route.distance / 1000, 1),
"# Fridges": itemsCount["fridge"],
"# ACs": itemsCount["ac"],
"# Dish Washers": itemsCount["dish_washer"],
"# Washing Machines": itemsCount["washing_machine"]
};
});
}
Insert cell
Insert cell
Insert cell
Insert cell
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.2 ? [] : [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
Type JavaScript, then Shift-Enter. Ctrl-space for more options. Arrow ↑/↓ to switch modes.

Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Select a data source…
Type Table, then Shift-Enter. Ctrl-space for more options.

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 % COLORS.length]
);
});
});
}
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: {
id: "ac",
name: "AC",
weight: 300,
skills: []
},
fridge: {
id: "fridge",
name: "Frige",
weight: 1000,
skills: [0]
},
dish_washer: {
id: "dish_washer",
name: "Dish Washer",
weight: 500,
skills: []
},
washing_machine: {
id: "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) {
const quantity = _.random(
ordersPerStore.LOWER_LIMIT,
ordersPerStore.UPPER_LIMIT
);
for (let i = 0; i < quantity; i++) {
orders.push({
storeId: store.id,
itemId: manufacturer.item,
quantity: 1,
city: store.city
});
}
}
return orders;
})
Insert cell
lol = STORES.flatMap((store) => {
const orders = [];
for (const manufacturer of MANUFACTURERS) {
if (_.random()) {
const quantity = _.random(0, 30);
for (let i = 0; i < quantity; i++) {
orders.push({
storeId: store.id,
itemId: manufacturer.item,
quantity: 1,
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