/** * Copyright (C) 2016 eBusiness Information * * This file is part of OSM Contributor. * * OSM Contributor is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OSM Contributor is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OSM Contributor. If not, see <http://www.gnu.org/licenses/>. */ package io.jawg.osmcontributor.rest.managers; import org.greenrobot.eventbus.EventBus; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import io.jawg.osmcontributor.ui.managers.PoiManager; import io.jawg.osmcontributor.database.dao.PoiNodeRefDao; import io.jawg.osmcontributor.model.entities.Poi; import io.jawg.osmcontributor.model.entities.PoiNodeRef; import io.jawg.osmcontributor.ui.events.map.PleaseLoadEditWaysEvent; import io.jawg.osmcontributor.rest.mappers.PoiMapper; import io.jawg.osmcontributor.rest.dtos.osm.NodeDto; import io.jawg.osmcontributor.rest.dtos.osm.OsmDto; import io.jawg.osmcontributor.rest.dtos.osm.WayDto; import io.jawg.osmcontributor.rest.clients.OsmRestClient; import io.jawg.osmcontributor.rest.clients.OverpassRestClient; import io.jawg.osmcontributor.rest.OSMProxy; import io.jawg.osmcontributor.utils.Box; import retrofit.RetrofitError; import retrofit.mime.TypedString; import timber.log.Timber; /** * Implementation of a {@link SyncWayManager} using an OpenStreetMap database as a backend. */ public class OSMSyncWayManager implements SyncWayManager { OSMProxy osmProxy; OverpassRestClient overpassRestClient; PoiMapper poiMapper; PoiManager poiManager; EventBus bus; PoiNodeRefDao poiNodeRefDao; OsmRestClient osmRestClient; public OSMSyncWayManager(OSMProxy osmProxy, OverpassRestClient overpassRestClient, PoiMapper poiMapper, PoiManager poiManager, EventBus bus, PoiNodeRefDao poiNodeRefDao, OsmRestClient osmRestClient) { this.osmProxy = osmProxy; this.overpassRestClient = overpassRestClient; this.poiMapper = poiMapper; this.poiManager = poiManager; this.bus = bus; this.poiNodeRefDao = poiNodeRefDao; this.osmRestClient = osmRestClient; } /** * Build the Overpass request to download all the ways included in the bounds. * * @param box The bounds of the request. * @return The Overpass request. */ private String generateOverpassRequestForWay(Box box) { StringBuilder cmplReq = new StringBuilder("(way"); // bounding box cmplReq.append("(") .append(box.getSouth()).append(",") .append(box.getWest()).append(",") .append(box.getNorth()).append(",") .append(box.getEast()) .append(");"); cmplReq.append(");out meta geom;"); return cmplReq.toString(); } /** * {@inheritDoc} */ @Override public void syncDownloadWay(final Box box) { Timber.d("Requesting overpass for ways"); OSMProxy.Result<Void> result = osmProxy.proceed(new OSMProxy.NetworkAction<Void>() { @Override public Void proceed() { String request = generateOverpassRequestForWay(box); OsmDto osmDto = overpassRestClient.sendRequest(new TypedString(request)); List<WayDto> wayDtoList = osmDto.getWayDtoList(); if (wayDtoList != null && wayDtoList.size() > 0) { Timber.d(" %d ways have been downloaded", wayDtoList.size()); List<Poi> poisFromOSM = poiMapper.convertDtosToPois(osmDto.getWayDtoList(), false); poiManager.mergeFromOsmPois(poisFromOSM, box); poiManager.deleteAllWaysExcept(poisFromOSM); bus.post(new PleaseLoadEditWaysEvent(true)); } else { Timber.d("No new ways found in the area"); } return null; } }); if (!result.isSuccess() && result.getRetrofitError() != null) { Timber.e(result.getRetrofitError(), "Retrofit error while trying to download area"); } } /** * {@inheritDoc} */ @Override public List<Poi> downloadPoiForWayEdition(List<Long> ids) { //get noderefs to update List<PoiNodeRef> poiNodeRefs = poiNodeRefDao.queryByPoiNodeRefIds(ids); //get Pois corresponding to noderefs to update List<Poi> pois = getPoiWaysToUpdate(); List<PoiNodeRef> poiNodeRefsToSave = new ArrayList<>(); Map<String, PoiNodeRef> poiNodeRefMap = new HashMap<>(); for (PoiNodeRef poiNodeRef : poiNodeRefs) { poiNodeRefMap.put(poiNodeRef.getNodeBackendId(), poiNodeRef); } //apply new lat lng to poi for (Poi poi : pois) { PoiNodeRef poiNodeRef = poiNodeRefMap.get(poi.getBackendId()); if (poiNodeRef != null) { poi.setLatitude(poiNodeRef.getLatitude()); poi.setLongitude(poiNodeRef.getLongitude()); poi.setUpdated(true); poiNodeRef.setUpdated(false); Long oldId = poiNodeRef.getOldPoiId(); if (oldId != null) { poiNodeRefDao.deleteById(oldId); } poiNodeRef.setOldPoiId(null); poiNodeRefsToSave.add(poiNodeRef); } } //save changes pois = poiManager.savePois(pois); poiManager.savePoiNodeRefs(poiNodeRefsToSave); return pois; } /** * Download from backend all the NodeRefs to update as POIs. * * @return The list of POIs to update. */ private List<Poi> getPoiWaysToUpdate() { final List<Long> ids = poiNodeRefDao.queryAllUpdated(); List<Poi> pois = new ArrayList<>(); if (ids != null) { OSMProxy.Result<List<Poi>> result = osmProxy.proceed(new OSMProxy.NetworkAction<List<Poi>>() { @Override public List<Poi> proceed() { List<Poi> pois; OsmDto osmDtoRetrievedNode = osmRestClient.getNode(formatIdList(ids)); if (osmDtoRetrievedNode != null) { List<NodeDto> nodeDtoList = osmDtoRetrievedNode.getNodeDtoList(); pois = poiMapper.convertDtosToPois(nodeDtoList, false); } else { pois = new ArrayList<>(); } return pois; } }); if (result.isSuccess()) { return result.getResult(); } if (result.getRetrofitError() != null) { RetrofitError e = result.getRetrofitError(); if (e.getResponse() != null && (e.getResponse().getStatus() == 404 || e.getResponse().getStatus() == 410)) { Timber.w("The poi with id %s couldn't be found on OSM", 1); } } } return pois; } /** * Transform a list of ids into a string where ids are separated from each over by a ",". * * @param ids The list of ids to transform. * @return The formatted list. */ private String formatIdList(List<Long> ids) { String idsStr = ""; int i = 1; for (Long id : ids) { if (id != null) { idsStr += id; if (i < ids.size()) { idsStr += ","; } } i++; } return idsStr; } }