package org.openlca.geo.kml; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.openlca.core.database.IDatabase; import org.openlca.core.database.NativeSql; import org.openlca.core.matrix.LongPair; import org.openlca.core.matrix.TechIndex; import org.openlca.util.BinUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Loads the KML features for a given set of process products from a database. */ public class KmlLoader implements IKmlLoader { private Logger log = LoggerFactory.getLogger(getClass()); protected final IDatabase database; protected final HashMap<Long, byte[]> locationKmz = new HashMap<>(); protected final HashMap<Long, LocationKml> resultByLocationId = new HashMap<>(); protected final Map<Long, Long> processLocations = new HashMap<>(); public KmlLoader(IDatabase database) { this.database = database; } @Override public final List<LocationKml> load(TechIndex index) { if (index == null) return Collections.emptyList(); try { loadProcessLocations(index); queryLocationTable(); List<LocationKml> results = new ArrayList<>(); for (int i = 0; i < index.size(); i++) { LongPair product = index.getProviderAt(i); LocationKml result = getFeatureResult(product.getFirst()); if (result == null) continue; if (!results.contains(result)) results.add(result); result.processProducts.add(product); } return results; } catch (Exception e) { log.error("failed to get KML data from database", e); return Collections.emptyList(); } } private void loadProcessLocations(TechIndex idx) throws Exception { Set<Long> processIds = idx.getProcessIds(); String query = "select id, f_location from tbl_processes"; NativeSql.on(database).query(query, rs -> { long id = rs.getLong("id"); if (!processIds.contains(id)) return true; long locationId = rs.getLong("f_location"); if (rs.wasNull()) return true; processLocations.put(id, locationId); return true; }); } private void queryLocationTable() throws Exception { String query = "select id, ref_id, kmz from tbl_locations"; NativeSql.on(database).query(query, rs -> { long id = rs.getLong("id"); if (!needToLoadLocation(id)) return true; byte[] kmz = rs.getBytes("kmz"); if (kmz != null) locationKmz.put(id, kmz); return true; }); } protected boolean needToLoadLocation(Long id) { return processLocations.containsValue(id); } protected LocationKml getFeatureResult(Long processId) { Long locationId = processLocations.get(processId); if (locationId == null) return null; LocationKml result = resultByLocationId.get(locationId); if (result != null) return result; byte[] locKmz = locationKmz.get(locationId); if (locKmz == null) return null; result = createResult(locationId, locKmz); resultByLocationId.put(locationId, result); return result; } protected final LocationKml createResult(long locationId, byte[] kmz) { if (kmz == null) return null; try { byte[] kmlBytes = BinUtils.unzip(kmz); String kml = new String(kmlBytes, "utf-8"); return new LocationKml(KmlFeature.parse(kml), locationId); } catch (Exception e) { log.error("failed to parse KMZ", e); return null; } } }