@Nullable
  private MutablePoi createPoiFromData(final fr.univmobile.backend.core.Poi dsPoi) {

    final int poiUid = dsPoi.getUid();

    final String coordinates = dsPoi.getCoordinates();
    final String latitude = substringBefore(coordinates, ",");
    final String longitude = substringAfter(coordinates, ",");

    if (isBlank(coordinates) || isBlank(latitude) || isBlank(longitude)) {
      return null;
    }

    final MutablePoi poi =
        DataBeans //
            .instantiate(MutablePoi.class) //
            .setId(poiUid) //
            .setName(dsPoi.getName()) //
            .setCoordinates(coordinates) //
            .setLatitude(latitude) //
            .setLongitude(longitude);

    if (dsPoi.getAddresses().length != 0) {
      poi.setAddress(dsPoi.getAddresses()[0].getFullAddress());
    }
    if (dsPoi.getUrls().length != 0) {
      poi.setUrl(dsPoi.getUrls()[0]);
    }
    if (dsPoi.getPhones().length != 0) {
      poi.setPhone(dsPoi.getPhones()[0]);
    }
    if (dsPoi.getFaxes().length != 0) {
      poi.setFax(dsPoi.getFaxes()[0]);
    }
    if (dsPoi.getAttachments().length != 0) {
      final String image = dsPoi.getAttachments()[0].getUrl();
      if (!image.startsWith("/upload")) {
        throw new NotImplementedException("Image URL: " + image);
      }
      poi.setImageUrl(composeURL(image));
      poi.setImageWidth(100).setImageHeight(100); // TODO get img
    } else {
      poi.setImageWidth(0).setImageHeight(0);
    }

    poi.setCommentsUrl(composeURL("/json/comments/poi" + poiUid));

    // UNIVERSITIES

    final String[] dsUniversities = dsPoi.getUniversities();

    final List<String> universityIds = new ArrayList<String>();

    for (final String dsUniversity : dsUniversities) {

      universityIds.add(dsUniversity);
    }

    poi.setUniversityIds(Iterables.toArray(universityIds, String.class));

    // END

    return poi;
  }
  @Override
  public Pois getPois(final double lat, final double lng) throws IOException {

    final MutablePois p = getPois();

    final PoiGroup[] poiGroups = p.getGroups();

    final Map<String, Poi[]> poiArrays = new HashMap<String, Poi[]>();

    for (final PoiGroup poiGroup : poiGroups) {

      final Poi[] pois = poiGroup.getPois();

      poiArrays.put(poiGroup.getGroupLabel(), pois);

      Arrays.sort(
          pois,
          new Comparator<Poi>() {

            @Override
            public int compare(final Poi p1, final Poi p2) {

              final double d1 =
                  getDistance(
                      Double.parseDouble(p1.getLatitude()),
                      Double.parseDouble(p1.getLongitude()),
                      lat,
                      lng);

              final double d2 =
                  getDistance(
                      Double.parseDouble(p2.getLatitude()),
                      Double.parseDouble(p2.getLongitude()),
                      lat,
                      lng);

              if (d1 < d2) {
                return -1;
              } else if (d1 > d2) {
                return 1;
              } else {
                return 0;
              }
            }
          });
    }

    Arrays.sort(
        poiGroups,
        new Comparator<PoiGroup>() {

          @Override
          public int compare(final PoiGroup g1, final PoiGroup g2) {

            final Poi[] pois1 = g1.getPois();
            final Poi[] pois2 = g2.getPois();

            if (pois1.length == 0 && pois2.length == 0) {
              return 0;
            } else if (pois1.length == 0) {
              return 1;
            } else if (pois2.length == 0) {
              return -1;
            }

            final double d1 =
                getDistance(
                    Double.parseDouble(pois1[0].getLatitude()),
                    Double.parseDouble(pois1[0].getLongitude()),
                    lat,
                    lng);

            final double d2 =
                getDistance(
                    Double.parseDouble(pois2[0].getLatitude()),
                    Double.parseDouble(pois2[0].getLongitude()),
                    lat,
                    lng);

            if (d1 < d2) {
              return -1;
            } else if (d1 > d2) {
              return 1;
            } else {
              return 0;
            }
          }
        });

    final MutableMapInfo mapInfo =
        DataBeans.instantiate(MutableMapInfo.class) //
            .setPreferredZoom(10) //
            .setLat(lat) //
            .setLng(lng);

    final MutablePois pois =
        DataBeans.instantiate(MutablePois.class) //
            .setMapInfo(mapInfo);

    for (final PoiGroup poiGroup : poiGroups) {

      final String groupLabel = poiGroup.getGroupLabel();

      final MutablePoiGroup poiGroup2 =
          DataBeans.instantiate(MutablePoiGroup.class).setGroupLabel(groupLabel); //

      pois.addToGroups(poiGroup2);

      for (final Poi poi : poiArrays.get(groupLabel)) {
        poiGroup2.addToPois(poi);
      }
    }

    return pois;
  }
  @Override
  public MutablePois getPois() throws IOException {

    log.debug("getPois()...");

    final Map<String, fr.univmobile.backend.core.Region> dsRegions //
        = regionDataSource.getAllBy(String.class, "uid");

    // final Map<String, PoiTree> dsPoitree //
    // = poitreeDataSource.getAllBy(String.class, "uid");

    final PoiGroup[] poiGroups = new PoiGroup[dsRegions.size()];

    int i = 0;

    final Set<fr.univmobile.backend.core.Region> sortedSet =
        new TreeSet<fr.univmobile.backend.core.Region>(
            new Comparator<fr.univmobile.backend.core.Region>() {

              @Override
              public int compare(
                  final fr.univmobile.backend.core.Region r1,
                  final fr.univmobile.backend.core.Region r2) {

                final Form form = Form.NFD;

                final String n1 = Normalizer.normalize(r1.getLabel(), form);
                final String n2 = Normalizer.normalize(r2.getLabel(), form);

                return n1.compareTo(n2);
              }
            });

    sortedSet.addAll(dsRegions.values());

    int markerIndex = 0;

    for (final fr.univmobile.backend.core.Region dsRegion : sortedSet) {

      final MutablePoiGroup poiGroup =
          DataBeans //
              .instantiate(MutablePoiGroup.class) //
              .setGroupLabel("Région : " + dsRegion.getLabel());

      poiGroups[i] = poiGroup;

      ++i;

      final String regionId = dsRegion.getUid();

      if (poitreeDataSource.isNullByUid(regionId)) { // TODO

        System.err.println("Cannot load PoiTree for region: " + regionId);

        continue;
      }

      final PoiTree poiTree = poitreeDataSource.getByUid(regionId);

      for (final PoiTree root : poiTree.getRoots()) {

        final int poiUid = Integer.parseInt(root.getUid());

        final fr.univmobile.backend.core.Poi dsPoi = poiDataSource.getByUid(poiUid);

        final MutablePoi poi = createPoiFromData(dsPoi);

        if (poi == null) {
          continue; // Skip empty POIs
        }

        poi.setMarkerType("green");
        poi.setMarkerIndex(Character.toString((char) ('A' + markerIndex)));

        markerIndex = (markerIndex + 1) % 26;

        poiGroup.addToPois(poi);
      }
    }

    final MutablePois pois = DataBeans.instantiate(MutablePois.class);

    for (final PoiGroup poiGroup : poiGroups) {

      pois.addToGroups(poiGroup);
    }

    return pois;
  }