Sharing my delivery system recipe

Draft Disclaimer: Please note that this article is currently in draft form and may undergo revisions before final publication. The content, including information, opinions, and recommendations, is subject to change and may not represent the final version. We appreciate your understanding and patience as we work to refine and improve the quality of this article. Your feedback is valuable in shaping the final release.

Language Mismatch Disclaimer: Please be aware that the language of this article may not match the language settings of your browser or device.
Do you want to read articles in English instead ?

Sharing my delivery system recipe

map_delivery

Features set

  • geocoding: get GPS coordinate from address (text)
  • directions: get itinerary from two addresses (text or GPS)
  • dynamic pricing

Pricing

so far static

  • area 1 : Centre-Ville 2.000 francs
  • area 2 : Pikine, Guédiawaye, Parcelles 2.500 francs
  • area 3 : Rufisque, Keur Massar 3.500 francs
  • area 4 : Bargny, Diamniadio 5.000 Francs
  • area x: price ?

Price = f(origin, destination)

The goal being to determine price per delivery. At first, I thought about having the pricing system to call the geocoding and direction services but separation of concerns principle. So from the addresses (origin, destination) -> geocoding for marker on map -> direction for itinerary -> pricing ()

What do you think the price for a delivery should depend on ? My answer to this question, is: distance and duration because traffic is something we do think about when taking a cab.

Price = f(distance, duration)

Data set 1

Direction service gave us

Price Address Distance Duration
2000 dakar plateau 7.6km 13m
2500 Pikine 15.7km 29m
2500 Guédiawaye 17km 30m
2500 Parcelles 10.5km 21m
3500 Rufisque 33.7km 43m
3500 Keur Massar 26.8km 43m
3500 Bargny 35.8km 45m
4000 Diamniadio 37.6km 41m
4000 Bargny 35.8km 45m
f(distance, duration) address distance duration

I am seeing this a linear function f(x,y) = ax + by + c

Numbers are in meters, seconds. I am taking only the average for the values above. That would give us

  • 7600a + (13 * 60)b + c = 2000

  • 14400a + (26 * 60 + 40)b + c = 2500

  • 30250a + (43 * 60)b + c = 3500

  • 36700a + (43 * 60)b + c = 5000

  • (A) 7600a + 780b + c = 2000

  • (B) 14400a + 1600b + c = 2500

  • (C) 30250a + 2580b + c = 3500

  • (D) 36700a + 2580b + c = 5000

B-A:6800a + 820b = 500 C-B:15850a + 980b = 1000

find a 6800a = 500 - 820b a = 1/6800(500 - 820b)

replace in C-B: 15850/6800(500 - 820b) + 980b = 1000 2.33(500 - 820b) + 980b = 1000 1165 - 930.6b = 1000 165 = 930.6b b = 0.1774

replace a a = 0.05215

7600a + 780b + c = 2000

c = 1465.37

a = 0.05215 b = 0.1774 c = 1465.37

price = 0.5215 * distance + 0.1774 * duration + 1465.37

Data set 2

Direction service gave us

Price Address Distance (km) Duration (minute)
1000 point E 2.1 7
1000 fann 2.1 5
1000 sacré coeur 2.3 9
1000 amitié 1.8 5
1000 castor 4.8 17
1000 dieuppeul 5.2 15
1000 HLM 4.1 12
1000 colobane 3.9 7
1000 fass 3.1 10
1000 grand dakar 2.8 9
1000 sicap liberté 4.4 11
1500 ouakam 3.3 8
1500 front de terre 5.5 16
1500 zone de captage 5.9 22
1500 liberté 6 4.1 9
1500 biscuiterie 4.2 15
1500 grand yoff 5.6 15
2000 Dakar plateau 7.6 13
2000 almadies 8.2 14
2000 mamelles 8.1 15
2500 Yarakh 4.8 16
2500 Dalifort 13.4 23
2500 Poste thiaroye 16.7 24
2500 Camberene 12.2 23
3000 Sicap mbao 27.2 39
3000 Grand mbao 26.9 39
3000 Guediawaye 17 30
3000 Pikine 15.7 29
3000 Thiaroye sur mer 15.7 27
3000 Guinaw rails 18.6 29
3500 Yeumbeul 20.4 41
3500 Keur massar 26.8 43
3500 Keur mbaye fall 23 35
3500 Malika 23.2 36
3500 Wakhinane nimzatt 18 30
4000 Rufisque 33.7 43
4000 Bargny 35.8 45
4000 Diamniadio 37.6 41
4000 Sebikhotane 50.3 67m
4000 Sangalkam 36.7 48
f(distance, duration) address distance duration
  • normalize data
const data = [
  [1000,'point E',2.1,7],
  [1000,'fann',2.1,5],
  [1000,'sacré coeur',2.3,9],
  [1000,'amitié',1.8,5],
  [1000,'castor',4.8,17],
  [1000,'dieuppeul',5.2,15],
  [1000,'HLM',4.1,12],
  [1000,'colobane',3.9,7],
  [1000,'fass',3.1,10],
  [1000,'grand dakar',2.8,9],
  [1000,'sicap liberté',4.4,11],
  [1500,'ouaka',3.3,8],
  [1500,'front de terre',5.5,16],
  [1500,'zone de captage',5.9 ,22],
  [1500,'liberté 6',4.1,9],
  [1500,'biscuiterie',4.2,15],
  [1500,'grand yoff',5.6,15],
  [2000,'Dakar plateau',7.6,13],
  [2000,'almadies',8.2,14],
  [2000,'mamelles',8.1,15],
  [2500,'Yarakh',4.8,16],
  [2500,'Dalifort',13.4,23],
  [2500,'Poste thiaroye',16.7,24],
  [2500,'Camberene',12.2,23],
  [3000,'Sicap mbao',27.2,39],
  [3000,'Grand mbao',26.9,39],
  [3000,'Guediawaye',17,30],
  [3000,'Pikine',15.7,29],
  [3000,'Thiaroye sur mer',15.7,27],
  [3000,'Guinaw rails',18.6,29],
  [3500,'Yeumbeul',20.4,41],
  [3500,'Keur massar',26.8,43],
  [3500,'Keur mbaye fall',23,35],
  [3500,'Malika',23.2,36],
  [3500,'Wakhinane nimzatt',18,30],
  [4000,'Rufisque',33.7,43],
  [4000,'Bargny',35.8,45],
  [4000,'Diamniadio',37.6,41],
  [4000,'Sebikhotane',50.3,67],
  [4000,'Sangalkam',36.7,48],
]

data.map(x => {
  return x[0] + ',' + x[2] + "\r\n"
})

const getColumnData = (data, columnIndex) => {
  return data.map(i => i[columnIndex])
}

const normalize = (columnData) => {
  const min = Math.min(...columnData)
  const max = Math.max(...columnData)
  return columnData.map(x => {
    return (x - min) / (max - min)
  })
}

const distances = getColumnData(data, 2)
const durations = getColumnData(data, 3)
normalize(distances)
normalize(durations)

| Price | Address | Distance (km) | Duration (minute) | 0.00000000e+00, 252.346741, -8.53235354e+00, 1.63897871e-01, -1.30360123e-03 price = (distance, duration) => 252.346741 * distance + -8.53235354 * duration - 0.00130360123

  • Price per bracket
Price Min Max Avg
1000 1.8 5.2 3.5
1500 1.8 5.2 3.5

Retrospectives

it has been fun so far to have the system calculate it but it does need some level of human intervention:

  • add smoothing for too small distance (ie 10m distance) or same point deliveries (ie dakar, dakar)
  • allow user to move marker point

Wrap up

Maths are useful for a developer. Inference to determine pricing from a set of data