package org.gcube.application.cms.sdi.plugins; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import org.bson.Document; import org.gcube.application.cms.plugins.IndexerPluginInterface; import org.gcube.application.cms.plugins.faults.IndexingException; import org.gcube.application.cms.plugins.faults.InitializationException; import org.gcube.application.cms.plugins.faults.InvalidPluginRequestException; import org.gcube.application.cms.plugins.faults.InvalidProfileException; import org.gcube.application.cms.plugins.implementations.IndexConstants; import org.gcube.application.cms.plugins.reports.IndexDocumentReport; import org.gcube.application.cms.plugins.reports.InitializationReport; import org.gcube.application.cms.plugins.reports.Report; import org.gcube.application.cms.plugins.requests.BaseRequest; import org.gcube.application.cms.plugins.requests.IndexDocumentRequest; import org.gcube.application.cms.sdi.Constants; import org.gcube.application.cms.sdi.engine.DBConstants; import org.gcube.application.cms.sdi.engine.PostgisIndexer; import org.gcube.application.cms.sdi.engine.PostgisTable; import org.gcube.application.cms.sdi.engine.bboxes.BBOXByCoordinatePaths; import org.gcube.application.cms.sdi.engine.bboxes.BBOXEvaluator; import org.gcube.application.cms.sdi.engine.bboxes.BBOXPathScanner; import org.gcube.application.cms.sdi.faults.SDIInteractionException; import org.gcube.application.cms.sdi.model.MappingObject; import org.gcube.application.cms.serialization.Serialization; import org.gcube.application.geoportal.common.model.JSONPathWrapper; import org.gcube.application.geoportal.common.model.configuration.Index; import org.gcube.application.geoportal.common.model.document.Project; import org.gcube.application.geoportal.common.model.document.filesets.sdi.GCubeSDILayer; import org.gcube.application.geoportal.common.model.document.identification.IdentificationReference; import org.gcube.application.geoportal.common.model.document.identification.SpatialReference; import org.gcube.application.geoportal.common.model.plugins.IndexerPluginDescriptor; import org.gcube.application.geoportal.common.model.plugins.PluginDescriptor; import org.gcube.application.geoportal.common.model.rest.ConfigurationException; import org.gcube.application.geoportal.common.model.useCaseDescriptor.UseCaseDescriptor; import org.gcube.portlets.user.uriresolvermanager.UriResolverManager; import org.gcube.portlets.user.uriresolvermanager.resolvers.query.GeoportalResolverQueryStringBuilder; import org.gcube.portlets.user.uriresolvermanager.resolvers.query.GeoportalResolverQueryStringBuilder.RESOLVE_AS; import org.geojson.Crs; import org.geojson.GeoJsonObject; import org.geojson.LngLatAlt; import org.geojson.Point; import com.vdurmont.semver4j.Semver; import lombok.extern.slf4j.Slf4j; @Slf4j public class SDIIndexerPlugin extends SDIAbstractPlugin implements IndexerPluginInterface { static final PluginDescriptor DESCRIPTOR = new PluginDescriptor(Constants.INDEXER_PLUGIN_ID, IndexerPluginDescriptor.INDEXER); static final ArrayList BBOX_EVALUATORS = new ArrayList<>(); static { DESCRIPTOR.setDescription("SDI Indexer. " + "Manage Centroids layers."); DESCRIPTOR.setVersion(new Semver("1.0.0")); BBOX_EVALUATORS.add(new BBOXPathScanner()); BBOX_EVALUATORS.add(new BBOXByCoordinatePaths()); } @Override public PluginDescriptor getDescriptor() { return DESCRIPTOR; } @Override public InitializationReport initInContext() throws InitializationException { InitializationReport report = new InitializationReport(); report.setStatus(Report.Status.OK); return report; } /** * Expected parameters : - indexName (unique) - workspace - flagInternalIndex as * Boolean; boolean - centroidRecord (OPT). * * @param request the request * @return the index document report * @throws InvalidPluginRequestException the invalid plugin request exception */ @Override public IndexDocumentReport index(IndexDocumentRequest request) throws InvalidPluginRequestException { log.info("Indexer {} : Serving Index Request {} ", this.getDescriptor().getId(), request); Project project = request.getDocument(); UseCaseDescriptor useCaseDescriptor = request.getUseCaseDescriptor(); Document requestArguments = request.getCallParameters(); log.debug("requestArguments is {} ", requestArguments); IndexDocumentReport report = new IndexDocumentReport(request); try { // ********* INIT INDEX // TODO CACHE PostgisIndexer indexer = getIndexer(useCaseDescriptor, requestArguments); Document profileConfiguration = getConfigurationFromProfile(useCaseDescriptor).getConfiguration(); log.debug("UseCaseDescriptor Configuration is {} ", profileConfiguration); // ************* PREPARE RECORD JSONPathWrapper documentNavigator = new JSONPathWrapper(Serialization.write(project)); Document centroidDoc = new Document(); if (requestArguments.containsKey("centroidRecord")) centroidDoc.putAll(requestArguments.get("centroidRecords", Document.class)); // DEFAULT VALUES centroidDoc.put(DBConstants.Defaults.PROJECT_ID, project.getId()); centroidDoc.put(DBConstants.Defaults.DISPLAYED, true); // Added by Francesco. Creating Gis Viewer Link as public or private Boolean isInternalIndex = null; try { isInternalIndex = requestArguments .getBoolean(IndexConstants.INDEX_PARAMETER_FLAGINTERNALINDEX); log.debug(IndexConstants.INDEX_PARAMETER_FLAGINTERNALINDEX + " read as {} ", isInternalIndex); } catch (Exception e) { // TODO: handle exception } log.info(IndexConstants.INDEX_PARAMETER_FLAGINTERNALINDEX + " is {} ", isInternalIndex); if (isInternalIndex != null) { try { log.debug("Trying to generate Geoportal Gis Link..."); // Contacting the Geoportal-Resolver via UriResolverManager UriResolverManager uriResolverManager = new UriResolverManager("GEO"); GeoportalResolverQueryStringBuilder builder = new GeoportalResolverQueryStringBuilder( project.getProfileID(), project.getId()); builder.scope(request.getContext().getId()); if (isInternalIndex) { builder.resolverAs(RESOLVE_AS.PRIVATE); } else { builder.resolverAs(RESOLVE_AS.PUBLIC); } Map params = builder.buildQueryParameters(); String shortLink = uriResolverManager.getLink(params, true); log.info("Geoportal GisViewer link is {} ", shortLink); centroidDoc.put(DBConstants.Defaults.GEOVIEWER_LINK_FIELD, shortLink); } catch (Exception e) { log.error("Error on creating the Geoportal GisViewer link for project id {}", project.getId(), e); } } // ********************** EVALAUTE POSITION log.debug("indexing UseCaseDescriptor {} : Evaluating Centroid... ", useCaseDescriptor.getId()); SpatialReference reference = null; List refs = project .getIdentificationReferenceByType(SpatialReference.SPATIAL_REFERENCE_TYPE); if (!refs.isEmpty()) { // Use existing Reference reference = Serialization.convert(refs.get(0), SpatialReference.class); log.debug("Using already defined spatial reference " + reference); GeoJsonObject object = Serialization.convert(reference.getGeoJson(), GeoJsonObject.class); GCubeSDILayer.BBOX bbox = GCubeSDILayer.BBOX.fromGeoJSON(object.getBbox()); log.info("Found declared BBOX {} ", bbox); Double pointX = (bbox.getMaxX() + bbox.getMinX()) / 2; Double pointY = (bbox.getMaxY() + bbox.getMinY()) / 2; String wkt = String.format("POINT (%1$f %2$f) ", pointX, pointY); centroidDoc.put("geom", wkt); } else { // unable to use current Spatial reference, try evaluating it log.debug("UseCaseDescriptor {} : Getting evaluation paths from useCaseDescriptor.. ", useCaseDescriptor.getId()); // for each configuration option try until found GCubeSDILayer.BBOX toSet = null; for (BBOXEvaluator evaluator : BBOX_EVALUATORS) { log.trace("UCD {}, Project {}. Evaluating BBOX with {}", useCaseDescriptor.getId(), project.getId(), evaluator); try { if (evaluator.isConfigured(profileConfiguration)) { toSet = evaluator.evaluate(profileConfiguration, useCaseDescriptor, documentNavigator); if (toSet != null) { log.info("UCD {}, Project {}. Evaluated BBOX {} with method {}", useCaseDescriptor.getId(), project.getId(), toSet, evaluator); break; } } } catch (Throwable t) { log.warn("UCD {}, Project {}. Exception with {}", useCaseDescriptor.getId(), project.getId(), evaluator, t); } } if (toSet == null) throw new IndexingException("No BBOX has been evaluated from project"); Double pointX = (toSet.getMaxX() + toSet.getMinX()) / 2; Double pointY = (toSet.getMaxY() + toSet.getMinY()) / 2; log.info("Evaluated BBOX {} ", toSet); String wkt = String.format("POINT (%1$f %2$f) ", pointX, pointY); // TODO support altitude Double pointZ = 0d; centroidDoc.put("geom", wkt); Point point = new Point(); point.setCoordinates(new LngLatAlt(pointX, pointY, pointZ)); point.setBbox(toSet.asGeoJSONArray()); // TODO Manage CRS point.setCrs(new Crs()); reference = new SpatialReference(Serialization.asDocument(point)); log.info("UCD {} project {}, Setting Spatial Reference {} ", useCaseDescriptor.getId(), project.getId(), Serialization.write(reference)); report.addIdentificationReference(reference); } // *********** Additional Values from useCaseDescriptor log.info("Setting additional values to centroid from mappings .."); for (MappingObject m : getMappings(useCaseDescriptor)) { List foundValues = documentNavigator.getByPath(m.getPath()); Object toSetValue = null; if (!foundValues.isEmpty()) { // NB CSV for multiple values StringBuilder b = new StringBuilder(); foundValues.forEach(o -> { // Parser returns list of list if (o instanceof Collection) ((Collection) o).forEach(v -> b.append(v + ",")); else b.append(o + ","); }); b.deleteCharAt(b.length() - 1); toSetValue = b.toString(); } log.trace("Setting {} = {} in centroid doc ", m.getName(), toSetValue); centroidDoc.put(m.getName(), toSetValue); } log.info("Inserting Centroid {} into {} ", Serialization.write(centroidDoc.toJson()), indexer); indexer.insert(centroidDoc); // Support to HIDE AND DISPLAY as requested by invoker if (requestArguments.containsKey("_toHideIds")) { List ids = Serialization.convert(requestArguments.get("_toHideIds"), List.class); log.info("Requested to hide centroids {} ", ids); indexer.updateIsVisible(false, ids); } if (requestArguments.containsKey("_toDisplayIds")) { List ids = Serialization.convert(requestArguments.get("_toDisplayIds"), List.class); log.info("Requested to display centroids {} ", ids); indexer.updateIsVisible(true, ids); } report.setStatus(Report.Status.OK); } catch (SDIInteractionException e) { log.error("Unable to index " + request, e); report.setStatus(Report.Status.ERROR); report.putMessage(e.getMessage()); } catch (Throwable t) { log.error("Unable to index " + request, t); report.setStatus(Report.Status.ERROR); report.putMessage(t.getMessage()); } finally { return report; } } private List getMappings(UseCaseDescriptor useCaseDescriptor) throws InvalidProfileException { return MappingObject.getMappingsFromUCD(useCaseDescriptor, getDescriptor().getId()); } @Override public IndexDocumentReport deindex(IndexDocumentRequest request) throws InvalidPluginRequestException { log.info("Indexer {} : Serving Index Request {} ", this.getDescriptor().getId(), request); IndexDocumentReport report = new IndexDocumentReport(request); try { PostgisIndexer indexer = getIndexer(request.getUseCaseDescriptor(), request.getCallParameters()); indexer.removeByFieldValue(PostgisIndexer.StandardFields.PROJECT_ID, request.getDocument().getId()); } catch (SDIInteractionException e) { log.error("Unable to index " + request, e); report.setStatus(Report.Status.ERROR); report.putMessage(e.getMessage()); } catch (Throwable t) { log.error("Unable to index " + request, t); report.setStatus(Report.Status.ERROR); report.putMessage(t.getMessage()); } finally { return report; } } /** * Expected parameters : workspace indexName * * @param request * @return * @throws ConfigurationException */ @Override public Index getIndex(BaseRequest request) throws ConfigurationException { try { return getIndexer(request.getUseCaseDescriptor(), request.getCallParameters()).getIndexConfiguration(); } catch (Throwable t) { throw new ConfigurationException("Unable to get Postgis index for ucd " + request.getUseCaseDescriptor().getId() + " in " + request.getContext(), t); } } // Inits index // TODO CACHE private PostgisIndexer getIndexer(UseCaseDescriptor ucd, Document params) throws ConfigurationException, SQLException, InvalidProfileException, SDIInteractionException { PostgisIndexer indexer = new PostgisIndexer(sdiCache.getObject(), ucd, postgisCache.getObject()); List mappingObjects = getMappings(ucd); List fields = PostgisTable.Field.fromMappings(mappingObjects); indexer.initIndex(params.getString(IndexConstants.INDEX_PARAMETER_INDEXNAME), fields, params.getString(IndexConstants.INDEX_PARAMETER_WORKSPACE), params.getString(IndexConstants.INDEX_PARAMETER_INDEXNAME)); return indexer; } }