diff --git a/README.md b/README.md
index 5deea9a8e817eeef122bad0ca8816e5bfc82b0ad..06dde6029d0e0c8250c6389628ab448921594483 100644
--- a/README.md
+++ b/README.md
@@ -696,6 +696,16 @@ This uses the PostcodeAnywhere UK Geocode service, this will geocode any string
 * **Terms of Service**: ?
 * **Limitations**: No restrictions on use
 
+#### Base Adresse Nationale FR (`:ban_data_gouv_fr`)
+
+* **API key**: none
+* **Quota**: none
+* **Region**: FR
+* **SSL support**: yes
+* **Languages**: en / fr
+* **Documentation**: https://adresse.data.gouv.fr/api/ (in french)
+* **Terms of Service**: https://adresse.data.gouv.fr/faq/ (in french)
+* **Limitations**: [Data licensed under Open Database License (ODbL) (you must provide attribution).](http://openstreetmap.fr/ban)
 
 ### IP Address Services
 
diff --git a/lib/geocoder/lookup.rb b/lib/geocoder/lookup.rb
index b704d7cd1d50540a3525d7629a8294e96590af6a..b5e9275a51b16dc6c6be3239c761d46a7aa30443 100644
--- a/lib/geocoder/lookup.rb
+++ b/lib/geocoder/lookup.rb
@@ -47,6 +47,7 @@ module Geocoder
         :okf,
         :postcode_anywhere_uk,
         :geoportail_lu,
+        :ban_data_gouv_fr,
         :test,
         :latlon
       ]
diff --git a/lib/geocoder/lookups/ban_data_gouv_fr.rb b/lib/geocoder/lookups/ban_data_gouv_fr.rb
new file mode 100644
index 0000000000000000000000000000000000000000..1aad701fac3e751f5b5c1c150cf8d602ed27c460
--- /dev/null
+++ b/lib/geocoder/lookups/ban_data_gouv_fr.rb
@@ -0,0 +1,130 @@
+# encoding: utf-8
+
+require 'geocoder/lookups/base'
+require 'geocoder/results/ban_data_gouv_fr'
+
+module Geocoder::Lookup
+  class BanDataGouvFr < Base
+
+    def name
+      "Base Adresse Nationale Française"
+    end
+
+    def map_link_url(coordinates)
+      "https://www.openstreetmap.org/#map=19/#{coordinates.join('/')}"
+    end
+
+    def query_url(query)
+      method = query.reverse_geocode? ? "reverse" : "search"
+      "#{protocol}://api-adresse.data.gouv.fr/#{method}/?" + url_query_string(query)
+    end
+
+    private # ---------------------------------------------------------------
+
+    def any_result?(query)
+      fetch_data(query)['features'].any?
+    end
+
+    def results(query)
+      if doc = fetch_data(query) and any_result?(doc)
+        [doc]
+      else
+        []
+      end
+    end
+
+    #### PARAMS ####
+
+    def query_url_params(query)
+      query_ban_datagouv_fr_params(query).merge(super)
+    end
+
+    def query_ban_datagouv_fr_params(query)
+      query.reverse_geocode? ? reverse_geocode_ban_fr_params(query) : search_geocode_ban_fr_params(query)
+    end
+
+    #### SEARCH GEOCODING PARAMS ####
+    #
+    #  :q             =>    required, full text search param)
+
+    #  :limit         =>    force limit number of results returned by raw API
+    #                       (default = 5) note : only first result is taken
+    #                       in account in geocoder
+    #
+    #  :autocomplete  =>    pass 0 to disable autocomplete treatment of :q
+    #                       (default = 1)
+    #
+    #  :lat           =>    force filter results around specific lat/lon
+    #
+    #  :lon           =>    force filter results around specific lat/lon
+    #
+    #  :type          =>    force filter the returned result type
+    #                       (check results for a list of accepted types)
+    #
+    #  :postcode      =>    force filter results on a specific city post code
+    #
+    #  :citycode      =>    force filter results on a specific city UUID INSEE code
+    #
+    #  For up to date doc (in french only) : https://adresse.data.gouv.fr/api/
+    #
+    def search_geocode_ban_fr_params(query)
+      params = {
+        q: query.sanitized_text
+      }
+      unless (limit = query.options[:limit]).nil? || !limit_param_is_valid?(limit)
+        params[:limit] = limit.to_i
+      end
+      unless (autocomplete = query.options[:autocomplete]).nil? || !autocomplete_param_is_valid?(autocomplete)
+        params[:autocomplete] = autocomplete.to_s
+      end
+      unless (type = query.options[:type]).nil? || !type_param_is_valid?(type)
+        params[:type] = type.downcase
+      end
+      unless (postcode = query.options[:postcode]).nil? || !code_param_is_valid?(postcode)
+        params[:postcode] = postcode.to_s
+      end
+      unless (citycode = query.options[:citycode]).nil? || !code_param_is_valid?(citycode)
+        params[:citycode] = citycode.to_s
+      end
+      params
+    end
+
+    #### REVERSE GEOCODING PARAMS ####
+    #
+    #  :lat           =>    required
+    #
+    #  :lon           =>    required
+    #
+    #  :type          =>    force returned results type
+    #                       (check results for a list of accepted types)
+    #
+    def reverse_geocode_ban_fr_params(query)
+      lat_lon = query.coordinates
+      params = {
+          lat: lat_lon.first,
+          lon: lat_lon.last
+      }
+      unless (type = query.options[:type]).nil? || !type_param_is_valid?(type)
+        params[:type] = type.downcase
+      end
+      params
+    end
+
+    def limit_param_is_valid?(param)
+      param.to_i.positive?
+    end
+
+    def autocomplete_param_is_valid?(param)
+      [0,1].include?(param.to_i)
+    end
+
+    def type_param_is_valid?(param)
+      %w(housenumber street locality village town city).include?(param.downcase)
+    end
+
+    def code_param_is_valid?(param)
+      (1..99999).include?(param.to_i)
+    end
+
+  end
+end
diff --git a/lib/geocoder/results/ban_data_gouv_fr.rb b/lib/geocoder/results/ban_data_gouv_fr.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0b936b0431237aeaf2e25ecbedd63b8b4e233f4c
--- /dev/null
+++ b/lib/geocoder/results/ban_data_gouv_fr.rb
@@ -0,0 +1,257 @@
+# encoding: utf-8
+require 'geocoder/results/base'
+
+module Geocoder::Result
+  class BanDataGouvFr < Base
+
+    #### BASE METHODS ####
+
+    def self.response_attributes
+      %w[limit attribution version licence type features]
+    end
+
+    response_attributes.each do |a|
+      unless method_defined?(a)
+        define_method a do
+          @data[a]
+        end
+      end
+    end
+
+    #### BEST RESULT ####
+
+    def result
+      features[0] if features.any?
+    end
+
+    #### GEOMETRY ####
+
+    def geometry
+      result['geometry'] if result
+    end
+
+    def precision
+      geometry['type'] if geometry
+    end
+
+    def coordinates
+      coords = geometry["coordinates"]
+      return [coords[1].to_f, coords[0].to_f]
+    end
+
+    #### PROPERTIES ####
+
+    # List of raw attrbutes returned by BAN data gouv fr API:
+    #
+    #   :id           =>    [string] UUID of the result, said to be not stable
+    #                       atm, based on IGN reference (Institut national de
+    #                       l'information géographique et forestière)
+    #
+    #   :type         =>    [string] result type (housenumber, street, city,
+    #                       town, village, locality)
+    #
+    #   :score        =>    [float] value between 0 and 1 giving result's
+    #                       relevancy
+    #
+    #   :housenumber  =>    [string] street number and extra information
+    #                       (bis, ter, A, B)
+    #
+    #   :street       =>    [string] street name
+    #
+    #   :name         =>    [string] housenumber and street name
+    #
+    #   :postcode     =>    [string] city post code (used for mails by La Poste,
+    #                       beware many cities got severeal postcodes)
+    #
+    #   :citycode     =>    [string] city code (INSEE reference,
+    #                       consider it as a french institutional UUID)
+    #
+    #   :city         =>    [string] city name
+    #
+    #   :context      =>    [string] department code, department name and
+    #                       region code
+    #
+    #   :label        =>    [string] full address without state, country name
+    #                       and country code
+    #
+    # CITIES ONLY PROPERTIES
+    #
+    #   :adm_weight   =>    [string] administrative weight (importance) of
+    #                       the city
+    #
+    #   :population   =>    [float] number of inhabitants with a 1000 factor
+    #
+    # For up to date doc (in french only) : https://adresse.data.gouv.fr/api/
+    #
+    def properties
+      result['properties'] if result
+    end
+
+    # List of usable Geocoder results' methods
+    #
+    #   score                   =>    [float] result relevance 0 to 1
+    #
+    #   location_id             =>    [string] location's IGN UUID
+    #
+    #   result_type             =>    [string] housenumber / street / city
+    #                                 / town / village / locality
+    #
+    #   international_address   =>    [string] full address with country code
+    #
+    #   national_address        =>    [string] full address with country code
+    #
+    #   street_address          =>    [string] housenumber + extra inf
+    #                                 + street name
+    #
+    #   street_number           =>    [string] housenumber + extra inf
+    #                                 (bis, ter, etc)
+    #
+    #   street_name             =>    [string] street's name
+    #
+    #   city_name               =>    [string] city's name
+    #
+    #   city_code               =>    [string] city's INSEE UUID
+    #
+    #   postal_code             =>    [string] city's postal code (used for mails)
+    #
+    #   context                 =>    [string] city's department code, department
+    #                                  name and region name
+    #
+    #   demartment_name         =>    [string] city's department name
+    #
+    #   department_code         =>    [string] city's department INSEE UUID
+    #
+    #   region_name             =>    [string] city's region name
+    #
+    #   population              =>    [string] city's inhabitants count
+    #
+    #   administrative_weight   =>    [integer] city's importance on a scale
+    #                                 from 6 (capital city) to 1 (regular village)
+    #
+    def score
+      properties['score']
+    end
+
+    def location_id
+      properties['id']
+    end
+
+    # Types
+    #
+    #   housenumber
+    #   street
+    #   city
+    #   town
+    #   village
+    #   locality
+    #
+    def result_type
+      properties['type']
+    end
+
+    def international_address
+      "#{national_address}, #{country}"
+    end
+
+    def national_address
+      properties['label']
+    end
+
+    def street_address
+      properties['name']
+    end
+
+    def street_number
+      properties['housenumber']
+    end
+
+    def street_name
+      properties['street']
+    end
+
+    def city_name
+      properties['city']
+    end
+
+    def city_code
+      properties['citycode']
+    end
+
+    def postal_code
+      properties['postcode']
+    end
+
+    def context
+      properties['context'].split(/,/).map(&:strip)
+    end
+
+    def department_code
+      context[0] if context.length > 0
+    end
+
+    # Monkey logic to handle fact Paris is both a city and a department
+    # in ÃŽle-de-France region
+    def department_name
+      if context.length > 1
+        if context[1] == "ÃŽle-de-France"
+          "Paris"
+        else
+          context[1]
+        end
+      end
+    end
+
+    def region_name
+      if context.length == 2 && context[1] == "ÃŽle-de-France"
+        context[1]
+      elsif context.length > 2
+        context[2]
+      end
+    end
+
+    def country
+      "France"
+    end
+
+    # Country code types
+    #    FR : France
+    #    GF : Guyane Française
+    #    RE : Réunion
+    #    NC : Nouvelle-Calédonie
+    #    GP : Guadeloupe
+    #    MQ : Martinique
+    #    MU : Maurice
+    #    PF : Polynésie française
+    #
+    # Will need refacto to handle different country codes, but BAN API
+    # is currently mainly designed for geocode FR country code addresses
+    def country_code
+      "FR"
+    end
+
+    #### ALIAS METHODS ####
+
+    alias_method :address, :international_address
+    alias_method :street, :street_name
+    alias_method :city, :city_name
+    alias_method :state, :region_name
+    alias_method :state_code, :state
+
+    #### CITIES' METHODS ####
+
+    def population
+      (properties['population'].to_f * 1000).to_i if city?(result_type)
+    end
+
+    def administrative_weight
+      properties['adm_weight'].to_i if city?(result_type)
+    end
+
+    private
+
+    def city?(result_type)
+      %w(village town city).include?(result_type)
+    end
+
+  end
+end
diff --git a/test/fixtures/ban_data_gouv_fr_montpellier b/test/fixtures/ban_data_gouv_fr_montpellier
new file mode 100644
index 0000000000000000000000000000000000000000..5000353a707a45f0fce44ecd79842ff448369a75
--- /dev/null
+++ b/test/fixtures/ban_data_gouv_fr_montpellier
@@ -0,0 +1,125 @@
+{
+  "limit": 5,
+  "attribution": "BAN",
+  "version": "draft",
+  "licence": "ODbL 1.0",
+  "query": "Montpellier",
+  "type": "FeatureCollection",
+  "features": [
+    {
+      "geometry": {
+        "type": "Point",
+        "coordinates": [
+          3.875521,
+          43.611024
+        ]
+      },
+      "properties": {
+        "citycode": "34172",
+        "adm_weight": "5",
+        "name": "Montpellier",
+        "city": "Montpellier",
+        "postcode": "34080",
+        "context": "34, Hérault, Languedoc-Roussillon",
+        "score": 0.9785090909090908,
+        "label": "Montpellier",
+        "id": "34172_34080",
+        "type": "city",
+        "population": "255.1"
+      },
+      "type": "Feature"
+    },
+    {
+      "geometry": {
+        "type": "Point",
+        "coordinates": [
+          3.875521,
+          43.611024
+        ]
+      },
+      "properties": {
+        "citycode": "34172",
+        "adm_weight": "5",
+        "name": "Montpellier",
+        "city": "Montpellier",
+        "postcode": "34000",
+        "context": "34, Hérault, Languedoc-Roussillon",
+        "score": 0.9785090909090908,
+        "label": "Montpellier",
+        "id": "34172",
+        "type": "city",
+        "population": "255.1"
+      },
+      "type": "Feature"
+    },
+    {
+      "geometry": {
+        "type": "Point",
+        "coordinates": [
+          3.875521,
+          43.611024
+        ]
+      },
+      "properties": {
+        "citycode": "34172",
+        "adm_weight": "5",
+        "name": "Montpellier",
+        "city": "Montpellier",
+        "postcode": "34090",
+        "context": "34, Hérault, Languedoc-Roussillon",
+        "score": 0.9785090909090908,
+        "label": "Montpellier",
+        "id": "34172_34090",
+        "type": "city",
+        "population": "255.1"
+      },
+      "type": "Feature"
+    },
+    {
+      "geometry": {
+        "type": "Point",
+        "coordinates": [
+          3.875521,
+          43.611024
+        ]
+      },
+      "properties": {
+        "citycode": "34172",
+        "adm_weight": "5",
+        "name": "Montpellier",
+        "city": "Montpellier",
+        "postcode": "34070",
+        "context": "34, Hérault, Languedoc-Roussillon",
+        "score": 0.9785090909090908,
+        "label": "Montpellier",
+        "id": "34172_34070",
+        "type": "city",
+        "population": "255.1"
+      },
+      "type": "Feature"
+    },
+    {
+      "geometry": {
+        "type": "Point",
+        "coordinates": [
+          -0.744328,
+          45.634502
+        ]
+      },
+      "properties": {
+        "citycode": "17244",
+        "adm_weight": "1",
+        "name": "Montpellier-de-Médillan",
+        "city": "Montpellier-de-Médillan",
+        "postcode": "17260",
+        "context": "17, Charente-Maritime, Poitou-Charentes",
+        "score": 0.825,
+        "label": "Montpellier-de-Médillan",
+        "id": "17244",
+        "type": "village",
+        "population": "0.6"
+      },
+      "type": "Feature"
+    }
+  ]
+}
diff --git a/test/fixtures/ban_data_gouv_fr_no_results b/test/fixtures/ban_data_gouv_fr_no_results
new file mode 100644
index 0000000000000000000000000000000000000000..3c2e78333984f1e07799b09f26ae8747ed4a148c
--- /dev/null
+++ b/test/fixtures/ban_data_gouv_fr_no_results
@@ -0,0 +1,9 @@
+{
+  "limit": 5,
+  "attribution": "BAN",
+  "version": "draft",
+  "licence": "ODbL 1.0",
+  "query": "oozpuip",
+  "type": "FeatureCollection",
+  "features": []
+}
diff --git a/test/fixtures/ban_data_gouv_fr_no_reverse_results b/test/fixtures/ban_data_gouv_fr_no_reverse_results
new file mode 100644
index 0000000000000000000000000000000000000000..657d238f46ad1dde95e4e9a06a97b931b95161cb
--- /dev/null
+++ b/test/fixtures/ban_data_gouv_fr_no_reverse_results
@@ -0,0 +1,8 @@
+{
+  "limit": 1,
+  "attribution": "BAN",
+  "version": "draft",
+  "licence": "ODbL 1.0",
+  "type": "FeatureCollection",
+  "features": []
+}
diff --git a/test/fixtures/ban_data_gouv_fr_paris b/test/fixtures/ban_data_gouv_fr_paris
new file mode 100644
index 0000000000000000000000000000000000000000..a1e46a0938d5d962bfe4a296201ac97f99095eef
--- /dev/null
+++ b/test/fixtures/ban_data_gouv_fr_paris
@@ -0,0 +1,117 @@
+{
+  "limit": 5,
+  "attribution": "BAN",
+  "version": "draft",
+  "licence": "ODbL 1.0",
+  "query": "Paris",
+  "type": "FeatureCollection",
+  "features": [
+    {
+      "geometry": {
+        "type": "Point",
+        "coordinates": [
+          2.3469,
+          48.8589
+        ]
+      },
+      "properties": {
+        "adm_weight": "6",
+        "citycode": "75056",
+        "name": "Paris",
+        "city": "Paris",
+        "postcode": "75000",
+        "context": "75, ÃŽle-de-France",
+        "score": 1,
+        "label": "Paris",
+        "id": "75056",
+        "type": "city",
+        "population": "2244"
+      },
+      "type": "Feature"
+    },
+    {
+      "geometry": {
+        "type": "Point",
+        "coordinates": [
+          3.564293,
+          45.766413
+        ]
+      },
+      "properties": {
+        "citycode": "63125",
+        "postcode": "63120",
+        "name": "Paris",
+        "city": "Courpière",
+        "context": "63, Puy-de-Dôme, Auvergne",
+        "score": 0.8255363636363636,
+        "label": "Paris 63120 Courpière",
+        "id": "63125_B221_03549b",
+        "type": "locality"
+      },
+      "type": "Feature"
+    },
+    {
+      "geometry": {
+        "type": "Point",
+        "coordinates": [
+          1.550208,
+          44.673592
+        ]
+      },
+      "properties": {
+        "citycode": "46138",
+        "postcode": "46240",
+        "name": "PARIS (Vaillac)",
+        "city": "Cœur de Causse",
+        "context": "46, Lot, Midi-Pyrénées",
+        "score": 0.824090909090909,
+        "label": "PARIS (Vaillac) 46240 Cœur de Causse",
+        "id": "46138_XXXX_6ee4ec",
+        "type": "street"
+      },
+      "type": "Feature"
+    },
+    {
+      "geometry": {
+        "type": "Point",
+        "coordinates": [
+          -0.526884,
+          43.762253
+        ]
+      },
+      "properties": {
+        "citycode": "40282",
+        "postcode": "40500",
+        "name": "Paris",
+        "city": "Saint-Sever",
+        "context": "40, Landes, Aquitaine",
+        "score": 0.8236181818181818,
+        "label": "Paris 40500 Saint-Sever",
+        "id": "40282_B237_2364e3",
+        "type": "locality"
+      },
+      "type": "Feature"
+    },
+    {
+      "geometry": {
+        "type": "Point",
+        "coordinates": [
+          0.157613,
+          47.336685
+        ]
+      },
+      "properties": {
+        "citycode": "37031",
+        "postcode": "37140",
+        "name": "Paris Buton",
+        "city": "Bourgueil",
+        "context": "37, Indre-et-Loire, Centre Val-de-Loire",
+        "score": 0.8235454545454545,
+        "label": "Paris Buton 37140 Bourgueil",
+        "id": "37031_B165_0a5e7a",
+        "type": "locality"
+      },
+      "type": "Feature"
+    }
+  ]
+}
diff --git a/test/fixtures/ban_data_gouv_fr_reverse b/test/fixtures/ban_data_gouv_fr_reverse
new file mode 100644
index 0000000000000000000000000000000000000000..b3a1b680c158f24762c42b3ecaf2c6cbe8ba475a
--- /dev/null
+++ b/test/fixtures/ban_data_gouv_fr_reverse
@@ -0,0 +1,33 @@
+{
+  "limit": 1,
+  "attribution": "BAN",
+  "version": "draft",
+  "licence": "ODbL 1.0",
+  "type": "FeatureCollection",
+  "features": [
+    {
+      "geometry": {
+        "type": "Point",
+        "coordinates": [
+          2.364375,
+          48.770639
+        ]
+      },
+      "properties": {
+        "street": "Rue du Lieutenant Alain le Coz",
+        "label": "4 Rue du Lieutenant Alain le Coz 94550 Chevilly-Larue",
+        "distance": 23,
+        "context": "94, Val-de-Marne, ÃŽle-de-France",
+        "id": "94021_1133_49638b",
+        "citycode": "94021",
+        "name": "4 Rue du Lieutenant Alain le Coz",
+        "city": "Chevilly-Larue",
+        "postcode": "94550",
+        "housenumber": "4",
+        "score": 0.9999997696809948,
+        "type": "housenumber"
+      },
+      "type": "Feature"
+    }
+  ]
+}
diff --git a/test/fixtures/ban_data_gouv_fr_rue_yves_toudic b/test/fixtures/ban_data_gouv_fr_rue_yves_toudic
new file mode 100644
index 0000000000000000000000000000000000000000..dea2d42c1d8cc95517bf0007faed42e551da4249
--- /dev/null
+++ b/test/fixtures/ban_data_gouv_fr_rue_yves_toudic
@@ -0,0 +1,33 @@
+{
+  "limit": 5,
+  "attribution": "BAN",
+  "version": "draft",
+  "licence": "ODbL 1.0",
+  "query": "13 rue yves toudic 75010 Paris",
+  "type": "FeatureCollection",
+  "features": [
+    {
+      "geometry": {
+        "type": "Point",
+        "coordinates": [
+          2.363473,
+          48.870131
+        ]
+      },
+      "properties": {
+        "citycode": "75110",
+        "postcode": "75010",
+        "name": "13 Rue Yves Toudic",
+        "housenumber": "13",
+        "city": "Paris",
+        "context": "75, ÃŽle-de-France",
+        "score": 0.9437454545454544,
+        "label": "13 Rue Yves Toudic 75010 Paris",
+        "id": "ADRNIVX_0000000270748760",
+        "type": "housenumber",
+        "street": "Rue Yves Toudic"
+      },
+      "type": "Feature"
+    }
+  ]
+}
diff --git a/test/test_helper.rb b/test/test_helper.rb
index 54a580dc3b7df20df85412da9bb204d51e8b78e6..f2e6f275005a7d8cab6a55f84fb0f50d9d4ef39e 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -361,6 +361,19 @@ module Geocoder
       end
     end
 
+    require 'geocoder/lookups/ban_data_gouv_fr'
+    class BanDataGouvFr
+      private
+      def fixture_prefix
+        "ban_data_gouv_fr"
+      end
+
+      def default_fixture_filename
+        "#{fixture_prefix}_rue_yves_toudic"
+      end
+    end
+
+
   end
 end
 
diff --git a/test/unit/lookups/ban_data_gouv_fr_test.rb b/test/unit/lookups/ban_data_gouv_fr_test.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8229c9882b417ffc7e8dcea261724bf7a94a7e45
--- /dev/null
+++ b/test/unit/lookups/ban_data_gouv_fr_test.rb
@@ -0,0 +1,135 @@
+# encoding: utf-8
+require 'test_helper'
+
+class BanDataGouvFrTest < GeocoderTestCase
+
+  def setup
+    Geocoder.configure(lookup: :ban_data_gouv_fr)
+  end
+
+  def test_query_for_geocode
+    query = Geocoder::Query.new('13 rue yves toudic, 75010 Paris')
+    lookup = Geocoder::Lookup.get(:ban_data_gouv_fr)
+    res = lookup.query_url(query)
+    assert_equal 'https://api-adresse.data.gouv.fr/search/?q=13+rue+yves+toudic%2C+75010+Paris', res
+  end
+
+  def test_query_for_reverse_geocode
+    query = Geocoder::Query.new([48.770639, 2.364375])
+    lookup = Geocoder::Lookup.get(:ban_data_gouv_fr)
+    res = lookup.query_url(query)
+    assert_equal 'https://api-adresse.data.gouv.fr/reverse/?lat=48.770639&lon=2.364375', res
+  end
+
+  def test_results_component
+    result = Geocoder.search('13 rue yves toudic, 75010 Paris').first
+    assert_equal 'ADRNIVX_0000000270748760', result.location_id
+    assert_equal 'housenumber', result.result_type
+    assert_equal 'Paris', result.city_name
+    assert_equal '13 Rue Yves Toudic 75010 Paris, France', result.international_address
+    assert_equal '13 Rue Yves Toudic 75010 Paris, France', result.address
+    assert_equal '13 Rue Yves Toudic 75010 Paris', result.national_address
+    assert_equal '13 Rue Yves Toudic', result.street_address
+    assert_equal '13', result.street_number
+    assert_equal 'Rue Yves Toudic', result.street
+    assert_equal 'Rue Yves Toudic', result.street_name
+    assert_equal 'Paris', result.city
+    assert_equal 'Paris', result.city_name
+    assert_equal '75110', result.city_code
+    assert_equal '75010', result.postal_code
+    assert_equal '75', result.department_code
+    assert_equal 'Paris', result.department_name
+    assert_equal 'ÃŽle-de-France', result.region_name
+    assert_equal 'France', result.country
+    assert_equal 'FR', result.country_code
+    assert_equal(48.870131, result.coordinates[0])
+    assert_equal(2.363473, result.coordinates[1])
+  end
+
+  def test_paris_special_business_logic
+    result = Geocoder.search('paris').first
+    assert_equal 'city', result.result_type
+    assert_equal '75000', result.postal_code
+    assert_equal 'France', result.country
+    assert_equal 'FR', result.country_code
+    assert_equal(2244000, result.population)
+    assert_equal 'Paris', result.city
+    assert_equal 'Paris', result.city_name
+    assert_equal '75056', result.city_code
+    assert_equal '75000', result.postal_code
+    assert_equal '75', result.department_code
+    assert_equal 'Paris', result.department_name
+    assert_equal 'ÃŽle-de-France', result.region_name
+    assert_equal(48.8589, result.coordinates[0])
+    assert_equal(2.3469, result.coordinates[1])
+  end
+
+  def test_city_result_methods
+    result = Geocoder.search('montpellier').first
+    assert_equal 'city', result.result_type
+    assert_equal '34080', result.postal_code
+    assert_equal '34172', result.city_code
+    assert_equal 'France', result.country
+    assert_equal 'FR', result.country_code
+    assert_equal(5, result.administrative_weight)
+    assert_equal(255100, result.population)
+    assert_equal '34', result.department_code
+    assert_equal 'Hérault', result.department_name
+    assert_equal 'Languedoc-Roussillon', result.region_name
+    assert_equal(43.611024, result.coordinates[0])
+    assert_equal(3.875521, result.coordinates[1])
+  end
+
+  def test_results_component_when_reverse_geocoding
+    result = Geocoder.search([48.770431, 2.364463]).first
+    assert_equal '94021_1133_49638b', result.location_id
+    assert_equal 'housenumber', result.result_type
+    assert_equal '4 Rue du Lieutenant Alain le Coz 94550 Chevilly-Larue, France', result.international_address
+    assert_equal '4 Rue du Lieutenant Alain le Coz 94550 Chevilly-Larue, France', result.address
+    assert_equal '4 Rue du Lieutenant Alain le Coz 94550 Chevilly-Larue', result.national_address
+    assert_equal '4 Rue du Lieutenant Alain le Coz', result.street_address
+    assert_equal '4', result.street_number
+    assert_equal 'Rue du Lieutenant Alain le Coz', result.street
+    assert_equal 'Rue du Lieutenant Alain le Coz', result.street_name
+    assert_equal 'Chevilly-Larue', result.city
+    assert_equal 'Chevilly-Larue', result.city_name
+    assert_equal '94021', result.city_code
+    assert_equal '94550', result.postal_code
+    assert_equal '94', result.department_code
+    assert_equal 'Val-de-Marne', result.department_name
+    assert_equal 'ÃŽle-de-France', result.region_name
+    assert_equal 'France', result.country
+    assert_equal 'FR', result.country_code
+    assert_equal(48.770639, result.coordinates[0])
+    assert_equal(2.364375, result.coordinates[1])
+  end
+
+  def test_no_reverse_results
+    result = Geocoder.search('no reverse results')
+    assert_equal 0, result.length
+  end
+
+  def test_actual_make_api_request_with_https
+    Geocoder.configure(use_https: true, lookup: :ban_data_gouv_fr)
+
+    require 'webmock/test_unit'
+    WebMock.enable!
+    stub_all = WebMock.stub_request(:any, /.*/).to_return(status: 200)
+
+    g = Geocoder::Lookup::BanDataGouvFr.new
+    g.send(:actual_make_api_request, Geocoder::Query.new('test location'))
+    assert_requested(stub_all)
+
+    WebMock.reset!
+    WebMock.disable!
+  end
+
+
+  private
+
+  def assert_country_code(result)
+    [:state_code, :country_code, :province_code].each do |method|
+      assert_equal 'FR', result.send(method)
+    end
+  end
+end