Saltar a contenido

API servicios Open data (CKAN y SOCRATA)

Ejemplo API CKAN

El API de CKAN https://docs.ckan.org/en/latest/api/index.html no ofrece diferentes niveles y métodos para poder buscar y filtrar datasets.

En este ejemplo utilizaremos el método resource_search para buscar datasets en cualquier portal de CKAN

Para buscar en portales CKAN necesitamos saber la URL del portal , ejemplo https://demo.ckan.org y añadir el path del método a utilizar /api/3/action/resource_search?

https://demo.ckan.org/api/3/action/resource_search?

Ver ejemplos online

Creación de un buscador

  • Dentro de nuestro espacio de trabajo creamos un archivo con el nombre de ckan.html.

  • Abrimos el archivo ckan.html con un editor de texto y copiamos el siguiente código.

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>
    Basic sample Resource Search API CKAN
  </title>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet"
    crossorigin="anonymous">
  <script src="https://code.jquery.com/jquery-3.6.1.min.js" crossorigin="anonymous"></script>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"
    crossorigin="anonymous"></script>
  <style>
    #results {
      width: 100%;
      background-color: #f2f2f2;
      margin: 5px;
    }
  </style>
</head>
<body>
</body>
</html>
* Abrimos el archivo ckan.html en el navegador.

  • Añadimos dentro del tag <body> la maquetación HTML
  <div class="container">
    <h3>Resource Search example </h3>
    <p> Package Search <br>
      <a target="_blank" href="https://docs.ckan.org/en/latest/api/">https://docs.ckan.org/en/latest/api/</a>
    </p>
    <form id="_form">
      <div class="form-group">
        <label for="url_ckan">Url:</label>

        <select id="url_ckan">
          <option value="https://demo.ckan.org/api/3/action/resource_search?">ckan.org</option>
          <option value="https://old.datahub.io/api/3/action/resource_search?">old.datahub.io</option>
          <!-- añadimos BCN opendata -->
                    <!--
          <option value="https://opendata-ajuntament.barcelona.cat/data/api/3/action/resource_search?">OpenBCN</option>
                    -->
        </select>
      </div>
      <div class="form-group">
        <label for="text_filter_ckan"> Filter <u>(name, descripton, format )</u></label>
        <input type="text" class="form-control" id="text_filter_ckan" value="name:wifi" placeholder="text filter">
      </div>
    </form>
    <form class="form-inline">
      <div class="form-group">
        <label for="num_results_ckan">Num results</label>
        <input type="number" size="3" class="form-control" id="num_results_ckan" value="5">
      </div>

    </form>
    <form>
      <div class="form-group">
        <button id="bt_send" type="button" class="btn btn-default btn-success">Send</button>
      </div>
    </form>
    <hr>
    <div id="results"></div>
    <div id="mygrid" style="height: 500px"></div>
  </div>
  • Abrimos el archivo ckan.html en el navegador.

  • Añadimos justo encima del tag </body> el siguiente código javascript

    <script>
    $.ajaxSetup({
      cache: true
    });
    $(document).ready(function () {
      $('#bt_send').on('click', function () {
        sendRequest();
      });

      $("#_form").submit(function (event) {
        sendRequest();
        event.preventDefault();
      });


      function sendRequest() {
        var data = {
          rows: $('#num_results_ckan').val(),
          query: $('#text_filter_ckan').val()
        };
        $.ajax({
          url: $('#url_ckan').val(),
          data: data,
          dataType: 'jsonp',
          success: function (data) {
            if (data.success) {
              $('#results').html('Total results found: ' + data.result.count);
              $('#mygrid').html('');

              if (data.result.count >= 1) {
                $('#mygrid').append('<ul>');
                $.each(data.result.results, function (index, value) {
                  console.log(value);
                  $('#mygrid').append('<li>' + value.name +': <a href="' + value.url + '">' + value.url +
                    '</a>');

                  $('#mygrid').append('</li>');
                });
                $('#mygrid').append('</ul>');

              }

            } else {
              $('#results').html("An error occured: " + data.error.message);
            }
          },
          error: function (xhr) {
            $('#results').html("An error occured: " + xhr.status + " " + xhr.statusText);
          }

        });
      }

    });
  </script>
  • Abrimos página ckan.html y lanzamos búsquedas

Question

Ejercicio 1: Añadir una o más URLs de otros portales de CKAN, por ejemplo OpenDataBCN

Question

Ejercicio 2: ¿Cómo haríamos para qué en los resultados apareciera la fecha de creación del dato?

Question

Ejercicio 3: Buscamos name:accidents y descargamos CSV accidentes

Ejemplos API SOCRATA

El API de Socrata https://dev.socrata.com no ofrece diferentes niveles y métodos para poder buscar y filtra datasets.

En este primer ejemplo utilizaremos la Discovery API https://socratadiscovery.docs.apiary.io/ para buscar datasets en cualquier portal de Socrata

Creación de un buscador

  • Creamos un archivo con el nombre de socrata.html.

  • Abrimos el archivo socrata.html con un editor de texto y copiamos el siguiente código.

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>
    Basic sample API Discovery SOCRATA
  </title>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet"
    crossorigin="anonymous">
  <script src="https://code.jquery.com/jquery-3.6.1.min.js" crossorigin="anonymous"></script>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"
    crossorigin="anonymous"></script>
  <style>
    #results {
      width: 100%;
      background-color: #f2f2f2;
      margin: 5px;
    }
  </style>
</head>
<body>
</body>
</html>
  • Abrimos el archivo socrata.html en el navegador.

  • Añadimos dentro del tag <body> la maquetación HTML.

    <div class="container">
    <h3>SOCRATA Resource Search example </h3>
    <p>Discovery API <br>
      <a target="_blank" href="https://socratadiscovery.docs.apiary.io/#">https://socratadiscovery.docs.apiary.io/#</a>
    </p>
    <form>
      <div class="form-group">
        <div class="radio">
          <label>
            <input type="radio" name="optionsRadios" id="optionsRadios1"
              value="https://api.eu.socrata.com/api/catalog/v1" checked>
            EU API Discovery
          </label>
        </div>
        <div class="radio">
          <label>
            <input type="radio" name="optionsRadios" id="optionsRadios2"
              value="https://api.us.socrata.com/api/catalog/v1">
            US API Discovery
          </label>
        </div>
      </div>
      <div class="form-group">
        <label for="text_filter_socrata"> Filter <u></u></label>
        <input type="text" class="form-control" id="text_filter_socrata" value="Contracts" placeholder="text filter">
      </div>
    </form>
    <form class="form-inline">
      <div class="form-group">
        <label for="num_results_socrata">Num results</label>
        <input type="number" size="3" class="form-control" id="num_results_socrata" value="25">
      </div>

    </form>
    <form>
      <div class="form-group">
        <button id="bt_send" type="button" class="btn btn-default btn-success">Send</button>
      </div>
    </form>
    <hr>
    <div id="results"></div>
    <div id="mygrid" style="height: 500px"></div>
  </div>
  • Abrimos el archivo socrata.html en el navegador.

  • Añadimos justo encima del tag </body> el siguiente código javascript

    <script>
    $.ajaxSetup({
      cache: true
    });
    $(document).ready(function () {
      $('#bt_send').on('click', function () {
        sendRequest();
      });

      $('#text_filter_socrata').on('keypress', function (event) {
        if (event.which == 13) {
          sendRequest();
          event.preventDefault();
        }
      });

      function sendRequest() {
        var _data = {
          q: $('#text_filter_socrata').val(),
          limit: $('#num_results_socrata').val()
        };
        $.ajax({
          url: $('input:radio[name=optionsRadios]:checked').val(),
          data: _data,
          method: 'GET',
          dataType: 'json',
          success: function (data) {
            console.info(data);
            if (data) {
              $('#results').html('Total results found: ' + data.resultSetSize);
              $('#mygrid').html('');

              if (data.resultSetSize >= 1) {
                $('#mygrid').append('<ul>');
                $.each(data.results, function (index, value) {
                  $('#mygrid').append('<li><b>' + value.resource.name + '</b>(' + value.resource.type + '): <a target="_blank" href="' + value.link + '">' + value.link + '</a>');
                  $('#mygrid').append('</li>');
                });
                $('#mygrid').append('</ul>');
              }
            } else {
              $('#results').html("An error occured:");
            }
          },
          error: function (xhr) {
            $('#results').html("An error occured: " + xhr.status + " " + xhr.statusText);
          }
        });
      }
    });
  </script>
  • Abrimos página socrata.html y lanzamos búsquedas

Question

Ejercicio 1: ¿Cómo filtraríamos para qué sólo enseñara "assets" de tipo "map", only:map?

Creación de un mapa para ver resultados de Socrata

  • Creamos un archivo con el nombre de socrata_mapa.html.

  • Abrimos el archivo socrata_mapa.html con un editor de texto y copiamos el siguiente código.

<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8">
  <title>
    Basic Leaflet Map sample API Discovery SOCRATA
  </title>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet"
    crossorigin="anonymous">
  <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.3/dist/leaflet.css" crossorigin="" />

  <script src="https://code.jquery.com/jquery-3.6.1.min.js" crossorigin="anonymous"></script>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"
    crossorigin="anonymous"></script>
  <script src="https://unpkg.com/leaflet@1.9.3/dist/leaflet.js"
    integrity="sha256-WBkoXOwTeyKclOHuWtc+i2uENFpDZ9YPdf5Hf+D7ewM=" crossorigin=""></script>
  <script type="text/javascript"
    src="https://cdnjs.cloudflare.com/ajax/libs/leaflet-ajax/2.1.0/leaflet.ajax.min.js"></script>
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/spin.js/2.3.2/spin.min.js"></script>
  <script type="text/javascript"
    src="https://cdnjs.cloudflare.com/ajax/libs/Leaflet.Spin/1.1.2/leaflet.spin.min.js"></script>
  <style>
    #results {
      width: 100%;
      background-color: #f2f2f2;
      margin: 5px;
    }
  </style>
</head>
<body>
</body>
</html>
  • Añadimos dentro del tag <body> la maquetación HTML
    <div class="container">
    <h3>SOCRATA Maps Resources </h3>
    <div class="row">
      <div class="col-md-6">
        <p>Discovery API <br>
          <a target="_blank" href="https://socratadiscovery.docs.apiary.io/#">https://socratadiscovery.docs.apiary.io/#</a>
        </p>
        <form>
          <div class="form-group">
            <div class="radio">
              <label>
                <input type="radio" name="optionsRadios" id="optionsRadios1"
                  value="https://api.eu.socrata.com/api/catalog/v1" checked>
                EU API Discovery
              </label>
            </div>
            <div class="radio">
              <label>
                <input type="radio" name="optionsRadios" id="optionsRadios2"
                  value="https://api.us.socrata.com/api/catalog/v1">
                US API Discovery
              </label>
            </div>
          </div>
          <div class="form-group">
            <label for="text_filter_socrata"> Filter <u></u></label>
            <input type="text" class="form-control" id="text_filter_socrata" value="" placeholder="text filter">

            <div class="checkbox">
              <label><input type="checkbox" id="chk_transparencia" value="analisi.transparenciacatalunya.cat">Only
                https://analisi.transparenciacatalunya.cat</label>
            </div>
            <div> Filter : only=maps</div>
          </div>

        </form>
        <form class="form-inline">
          <div class="form-group">
            <label for="num_results_socrata">Num results</label>
            <input type="number" size="3" class="form-control" id="num_results_socrata" value="25">
          </div>

        </form>
        <form>
          <div class="form-group">
            <button id="bt_send" type="button" class="btn btn-default btn-success">Send</button>
          </div>
        </form>
        <hr>
        <div id="results"></div>
        <div id="mygrid" style="height: 365px;overflow:auto">
        </div>
      </div>
      <div class="col-md-6">
        <div id="map" style="width:100%;height:700px"></div>
      </div>
    </div>
  </div>
  • Abrimos el archivo socrata_mapa.html en el navegador.

  • Añadimos justo encima del tag </body> el siguiente código javascript

  <script>
    $.ajaxSetup({
      cache: true
    });
    var map;
    var geojsonLayer;
    var _LL;
    $(document).ready(function () {
      map = L.map('map').setView([41.6863, 1.8382], 8);
      esri = L.tileLayer(
        'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
        maxZoom: 17,
        minZoom: 1,
        attribution: 'Tiles © Esri',
      })

      osm = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
        maxZoom: 19,
        minZoom: 1,
        attribution: 'OSM'
      }).addTo(map);

      Stamen_Toner = L.tileLayer('http://{s}.tile.stamen.com/toner/{z}/{x}/{y}.png', {
        attribution: 'Map tiles by <a href="https://stamen.com">Stamen Design</a>, <a href="https://creativecommons.org/licenses/by/3.0">CC BY 3.0</a> &mdash; Map data &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
        subdomains: 'abcd',
        minZoom: 0,
        maxZoom: 20
      })

      var baseMaps = {
        "Orto": esri,
        "Mapa": osm,
        "Toner": Stamen_Toner
      };

      L.control.layers(baseMaps, null).addTo(map);
      L.control.scale().addTo(map);


      $('#bt_send').on('click', function () {
        sendRequest();
      });

      $('#text_filter_socrata').on('keypress', function (event) {
        if (event.which == 13) {
          sendRequest();
          event.preventDefault();
        }
      });

      $(document).on('click', '.btn-xs', function () {

        var attr = $(this).attr('data');
        if (attr && attr.indexOf('#') != -1) {
          var params = attr.split("#");
          var _url = 'https://' + params[1] + '/api/views.json?method=getByResourceName&name=' + params[0];
          $.ajax({
            url: _url,
            method: 'GET',
            dataType: 'json',
            success: function (data) {

              if (data.childViews) {
                var _url2 = 'https://' + params[1] + '/resource/' + data.childViews[0] + '.json?';
                //var _url2 = 'https://' + params[1] + '/api/geospatial/' + data.childViews[0] + '?method=export&format=GeoJSON';
                sendRequestGEOJSON(_url2, true);
              } else {
                var _url2 = 'https://' + params[1] + '/resource/' + params[0] + '.json?$limit=30';
                sendRequestGEOJSON(_url2, false);
              }
            },
            error: function (xhr) {
              $('#results').html("An error occured: " + xhr.status + " " + xhr.statusText);
            }

          });

        } else {
          alert("No resource available");
        }
      });

      function clearLayers() {
        if (geojsonLayer && map.hasLayer(geojsonLayer)) {
          map.removeLayer(geojsonLayer);
        };
        if (_LL && map.hasLayer(_LL)) {
          map.removeLayer(_LL);
        };
      }

      function sendRequestGEOJSON(_url2, isGeoJson) {
        map.spin(true);
        var stylePoint = {
          radius: 8,
          fillColor: "#ff7800",
          color: "#000",
          weight: 1,
          opacity: 1,
          fillOpacity: 0.8
        };
        clearLayers();
        $.ajax({
          type: "GET",
          url: _url2,
          // jsonp: "$jsonp",
          //  dataType: "jsonp",
          success: function (response) {
            if (isGeoJson) {

              geojsonLayer = L.geoJson(response, {
                style: function (feature) {
                  return {
                    weight: 2,
                    color: "#999",
                    opacity: 1,
                    fillColor: "#B0DE5C",
                    fillOpacity: 0.8
                  };
                },

                onEachFeature: popUp
              }).addTo(map);
              map.fitBounds(geojsonLayer.getBounds());
              map.spin(false);
            } else {
              _LL = L.featureGroup()

              for (var i = 0; i < response.length; i++) {
                var marker = response[i];

                if (response[i].location_1) {
                  L.circleMarker([response[i].location_1.latitude, response[i].location_1.longitude], stylePoint).addTo(_LL);
                } else if (response[i].location) {
                  L.circleMarker([response[i].location.latitude, response[i].location.longitude], stylePoint).addTo(_LL);
                } else {
                  $('#results').html("ERROR no locations found");
                  map.spin(false);

                }
              }

              _LL.addTo(map);
              map.panTo(_LL.getBounds().getCenter());
              map.spin(false);

            }
          },
          error: function (xhr) {
            $('#results').html("An error occured: " + xhr.status + " " + xhr.statusText);
            map.spin(false);
          }
        });
      }

      function popUp(f, l) {
        var out = [];
        if (f.properties) {
          for (key in f.properties) {
            out.push(key + ": " + f.properties[key]);
          }
          l.bindPopup(out.join("<br />"));
        }
      }

      function sendRequest() {
        var _data = {
          limit: $('#num_results_socrata').val(),
          only: 'maps'
        };
        console.info($('#chk_transparencia').attr('checked'));
        if ($('#chk_transparencia').attr('checked')) {
          _data.domains = $('#chk_transparencia').val();
        }
        if ($('#text_filter_socrata').val() != "") {
          _data.q = $('#text_filter_socrata').val();
        }

        $.ajax({
          url: $('input:radio[name=optionsRadios]:checked').val(),
          data: _data,
          method: 'GET',
          dataType: 'json',
          success: function (data) {
            console.info(data);
            if (data) {
              $('#results').html('Total results found: ' + data.resultSetSize);
              $('#mygrid').html('');

              if (data.resultSetSize >= 1) {
                var cList = $('<ul>').appendTo('#mygrid');
                $.each(data.results, function (index, value) {
                  $('<li class="li"><b>' + value.resource.name + ': </b>' +
                    '<a target="_blank" href="' + value.link + '"> Link </a> ' +
                    '<a class="btn btn-success btn-xs"  href="#" data="' + value.resource.id + '#' + value.metadata.domain + '">Map it</a>').appendTo(cList);

                });
              }

            } else {
              console.info(data);
              $('#results').html("An error occured:");
            }
          },
          error: function (xhr) {
            $('#results').html("An error occured: " + xhr.status + " " + xhr.statusText);
          }
        });
      }
    });
  </script>
  • Abrimos página socrat_mapa.html y lanzamos búsquedas

Question

Ejercicio 1: Añadimos attribution y download_count a los resultados

Question

Ejercicio 2: ¿Cambiamos colores y estilos del mapa?

Referencias

https://docs.ckan.org/en/latest/api/ https://ckan.org https://www.socrata.com https://docs.socratadiscovery.apiary.io