Franta – Občasník malého ajťáka

Domény, Hosting, Cestování

ElasticSearch – GeoIP – hledani dle statu/oblasti

Elastic Search

Posledni dobou vyuzivam hodne ChatGPT jako junior programatora po ruce pro scripty ktery jsem liny psat … nicmene se hodi i jako docela dobry konzultant pro veci, ktere jsou slozitejsi a clovek si neni jisty jak je resit, nebo ze jdou vubec resit. Dneska jsem diky nemu sprovoznil vyhledavani v datech (obrazky s geo souradnicema) dle zeme kde byl obrazek porizen.

Vedel jsem, ze Elastic ma typ objektu geo_point na ktery lze pak aplikovat hledani typu: Najdi me obrazky ktere jsou v 1km vzdalenosti od lokality LAT/LON. Tak jsem si rikal, jestli vlastne jde nejak hledat i v oblasti konkretni zeme. Zjistil jsem, ze ES podporuje tkzv geo_shape, tedy vydefinovane oblasti souradnicema, nicmene tyto oblasti si musim najit sam.

Pri hledani dal jsem zjistil, ze tyto souradnice jsou k dispozici na internetu z mnoha zdroju s ruznym vyberem presnosti a jsou ve formatu GeoJSON. Zdroj ktery jsem pouzil ja byl nakonec: https://github.com/simonepri/geo-maps/blob/master/info/countries-land.md

Jenze co s tim ? Preci nebudu ukladat souradnice do nejaky externi databaze abych pak vyplnoval dlouhe zadani to ES dotazu s geo_shape.

Diky ChatGPT jsem ale zjistil, ze ES podporuje nacitani Geo dat z jineho indexu, nez ve kterem hledam! Bingo!

Rozhodl jsem si tedy vytvorit index geoips s mappingem:

{
  "mappings": {
    "properties": {
      "coordinates": {
        "type": "geo_shape"
      },
      "country": {
        "type": "keyword"
      }
    }
  }
}

Bohuzel pro prilis striktni validaci ze strany geo_shape se mi nepodarilo naimportovat spravne vsechny GeoJSON jednotlivych zemi sveta a proto jsem se nakonec rozhodl upravit mapping tak aby odpovidal tomu co ES chce, ale zaroven me nebuzeroval s problemy:

{
  "mappings": {
    "properties": {
      "coordinates": {
        "properties": {
          "coordinates": {
            "type": "float"
          }
        }
      },
      "country": {
        "type": "keyword"
      }
    }
  }
}

Abych mohl GeoJSON do ES nahrat, potrebuju upravit trochu format puvodniho JSON a to aby obsahoval “country” a “coordinates” a nic vic, to jsem docilil pres python script od ChatGPT takto:

import json

# Načtení GeoJSON souboru
with open('Downloads/countries-land-10km.geo.json', 'r') as file:
    geojson = json.load(file)

# Vytvoření a uložení nového souboru
with open('country_geoip.json', 'w') as file:
    for feature in geojson['features']:
        country = feature['properties']['A3']  # předpokládáme, že A3 je kód země
        geometry = feature['geometry']

        # Vytvoření nového formátu
        new_format = {"country": country, "coordinates": geometry}

        # Převedení nového formátu na json string a zapsání do souboru
        file.write(json.dumps(new_format) + '\n')

A cilovy country_geoip.json jsem naimportoval do ES pres esbulk :

esbulk -skipbroken -index geoips -id country -server localhost:9200  -verbose country_geoip.json

A jak nakonec takove vyhledavani v ES podle Geo vypada ? Napriklad takto, kdy si vybiram vsechny dokumenty s geo lokaci v Singapuru 🙂

{
  "query": {
    "bool": {
      "must": {
        "match_all": {}
      },
      "filter": {
        "geo_shape": {
          "location": {
            "indexed_shape": {
              "index": "geoips",
              "id": "SGS",
              "path": "coordinates"
            }
          }
        }
      }
    }
  }
}

Kody jednotlivych statu lze najit napriklad na strankach Ministerstva Vnitra nebo v JSON formatu na Githubu. Urcite jsem mohl nahradit ID za 2-letter code nebo za nazev zeme rovnou v importu, ale to pro muj ucel nebylo nutne. Nicmene, zde je upraveny Python script, ktery nam z ISO-Alpha-3 udela ISO-Alpha-2:

import json

# Načtení GeoJSON souboru
with open('./Downloads/countries-land-10km.geo.json', 'r') as file:
    geojson = json.load(file)

# Načtení souboru s definicí zemí
with open('countries.json', 'r') as file:
    countries = json.load(file)

# Vytvoření slovníku pro rychlé vyhledávání alpha-2 kódů podle alpha-3 kódů
alpha2_dict = {country['alpha-3']: country['alpha-2'] for country in countries}

# Vytvoření a uložení nového souboru
with open('geoips.json', 'w') as file:
    for feature in geojson['features']:
        country_alpha3 = feature['properties']['A3']  # ISO_A3 je kód země
        geometry = feature['geometry']

        # Pokud existuje příslušný alpha-2 kód, nahradíme jím alpha-3 kód
        country_alpha2 = alpha2_dict.get(country_alpha3, country_alpha3)

        # Vytvoření nového formátu
        new_format = {"country": country_alpha2, "coordinates": geometry}

        # Převedení nového formátu na json string a zapsání do souboru
        file.write(json.dumps(new_format) + '\n')

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *

Tato stránka používá Akismet k omezení spamu. Podívejte se, jak vaše data z komentářů zpracováváme..