N-Ways
Documentation développeursEnlèvement par un livreur

Exemple : enlèvement par un livreur

Trouvez le point d’enlèvement à partir de ce que le client a tapé, menez-y le livreur en okada en évitant les zones d’interdiction, et affichez ce que rapporte la course.

Sur cette page

Un livreur reçoit un nouvel enlèvement : le client a tapé « Shoprite Ikeja », un code postal NIPOST ou un code GhanaPost. Cet écran trouve le lieu, choisit l’entrée du bâtiment quand N-Ways la connaît, calcule la course en okada jusque-là en contournant les zones d’interdiction des okadas, et affiche la durée du trajet et le tarif à prévoir.

Ce qu’il utilise

Utilisez une clé restreinte à votre site web (ou à votre application, dans une web view) et aux API Maps, Saisie semi-automatique, Géocodage et Itinéraires.

La page

pickup.html
<!doctype html>
<html lang="fr">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Enlèvement</title>
    <link rel="stylesheet" href="https://nways.ng/sdk/v1/maplibre-gl.css" />
    <style>
      body { margin: 0; font: 16px/1.4 system-ui, sans-serif }
      #map { height: 60vh }
      .panel { padding: 12px 16px }
      input { width: 100%; box-sizing: border-box; padding: 12px; font-size: 16px; border: 1px solid #b8c2bc; border-radius: 10px }
      #trip { margin-top: 12px }
      #trip b { font-size: 1.4em }
    </style>
  </head>
  <body>
    <div id="map"></div>
    <div class="panel">
      <label for="pickup">Enlèvement</label>
      <input id="pickup" placeholder="Adresse, code postal NIPOST ou code GhanaPost" />
      <div id="trip" aria-live="polite"></div>
    </div>
    <script type="module">
      import { attachAutocomplete, createClient, createMap, maplibregl } from 'https://nways.ng/sdk/v1/nways-maps.standalone.js'

      const key = 'VOTRE_CLE_API'
      const nways = createClient({ key })
      const map = createMap({ container: 'map', key, center: [3.3515, 6.6018], zoom: 13 })
      const trip = document.getElementById('trip')
      let pin = null

      // Où se trouve le livreur (Allen Avenue, Ikeja, si le téléphone ne le dit pas).
      const here = () =>
        new Promise((done) =>
          navigator.geolocation.getCurrentPosition(
            (p) => done({ lat: p.coords.latitude, lng: p.coords.longitude }),
            () => done({ lat: 6.6018, lng: 3.3515 }),
            { enableHighAccuracy: true, timeout: 8000 },
          ),
        )

      // La meilleure entrée d’un code postal NIPOST : une entrée vérifiée s’il y en a une.
      async function entranceOf(place) {
        if (!place.postcode) return null
        const building = await nways.postcode(place.postcode)
        const door = building.entrances?.find((e) => e.verified) ?? building.entrances?.[0]
        return door ? door.location : null
      }

      async function plan(place) {
        trip.textContent = 'Calcul de l’itinéraire…'
        const door = await entranceOf(place).catch(() => null)
        const target = door ?? place.location
        const [lng, lat] = target.coordinates
        pin?.remove()
        pin = new maplibregl.Marker({ color: '#0b7a3e' }).setLngLat([lng, lat]).addTo(map)

        try {
          const { routes } = await nways.route({ origin: await here(), destination: { lat, lng }, mode: 'okada', fares: true })
          const r = routes[0]
          const line = { type: 'Feature', properties: {}, geometry: r.geometry }
          if (map.getSource('trip')) map.getSource('trip').setData(line)
          else {
            map.addSource('trip', { type: 'geojson', data: line })
            map.addLayer({ id: 'trip', type: 'line', source: 'trip', layout: { 'line-cap': 'round', 'line-join': 'round' }, paint: { 'line-color': '#1a73e8', 'line-width': 6 } })
          }
          const box = r.geometry.coordinates.reduce((b, c) => b.extend(c), new maplibregl.LngLatBounds(r.geometry.coordinates[0], r.geometry.coordinates[0]))
          map.fitBounds(box, { padding: 48 })

          const fare = r.fares?.find((f) => f.mode === 'okada')
          const bans = r.vehicleRules?.avoided?.length ?? 0
          trip.innerHTML = ''
          trip.append(
            Object.assign(document.createElement('b'), { textContent: `${Math.round(r.durationS / 60)} min` }),
            ` · ${(r.distanceM / 1000).toFixed(1)} km`,
            fare ? ` · tarif ${fare.text}` : '',
            bans ? ` · évite ${bans} zone${bans > 1 ? 's' : ''} d’interdiction des okadas` : '',
            door ? ' · jusqu’à l’entrée' : '',
          )
        } catch (e) {
          // Pas d’itinéraire en okada (par exemple, le lieu d’enlèvement est isolé par des zones d’interdiction), ou une erreur de clé ou de quota.
          trip.textContent = /^(OKADA_BANNED|NO_ROUTE|NO_ROAD_ROUTE)/.test(e.reason ?? '') ? 'Pas d’itinéraire en okada jusque-là. Essayez en voiture.' : `Impossible de calculer la course (${e.reason ?? e.message}).`
        }
      }

      attachAutocomplete(document.getElementById('pickup'), {
        client: nways,
        near: { lat: 6.6018, lng: 3.3515 },
        onSelect: plan,
      })
    </script>
  </body>
</html>

Comment ça marche

  1. La recherche. attachAutocomplete propose des rues, des commerces, des repères et des codes pendant que le livreur tape, les plus proches en premier. Toute la recherche compte pour une seule session de saisie semi-automatique dans la facturation.
  2. La porte, pas le milieu du bâtiment. Une suggestion avec un postcode est un bâtiment doté d’un code NIPOST ; ses entrances disent où se trouve le portail, et celles marquées verified ont été confirmées par des gens sur place.
  3. Un itinéraire en okada. mode: 'okada' respecte les interdictions d’okada que N-Ways connaît ; vehicleRules.avoided liste celles que l’itinéraire a contournées. Si le lieu d’enlèvement est dans une zone d’interdiction, l’itinéraire s’arrête à sa limite et le signale dans vehicleRules.endBan.
  4. Le tarif. Avec fares: true, chaque itinéraire contient fares : une fourchette et un text comme 1 500–2 200 ₦, tirés de ce que les passagers ont payé pour des courses semblables.

L’adapter

  • Kekes et voitures. Utilisez mode: 'keke' ou 'drive' ; le tarif suit le mode.
  • Directement les codes postaux. Si vos clients donnent un code postal NIPOST, passez la recherche : destination: { postcode: 'LA08A09DB09' } mène à la meilleure entrée du bâtiment.
  • Dans une application native. Les mêmes appels existent dans les SDK Android et iOS.