/* * Constellation - An open source and standard compliant SDI * http://www.constellation-sdi.org * * Copyright 2014 Geomatys. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.constellation.map.featureinfo; import org.constellation.ws.MimeType; import org.geotoolkit.coverage.GridSampleDimension; import org.geotoolkit.display.PortrayalException; import org.geotoolkit.display2d.canvas.RenderingContext2D; import org.geotoolkit.display2d.primitive.ProjectedCoverage; import org.geotoolkit.display2d.primitive.ProjectedFeature; import org.geotoolkit.display2d.primitive.SearchAreaJ2D; import org.geotoolkit.display2d.service.CanvasDef; import org.geotoolkit.display2d.service.SceneDef; import org.geotoolkit.display2d.service.ViewDef; import org.geotoolkit.feature.ComplexAttribute; import org.geotoolkit.feature.Feature; import org.geotoolkit.feature.Property; import org.geotoolkit.feature.type.FeatureType; import org.geotoolkit.feature.type.PropertyDescriptor; import org.geotoolkit.map.FeatureMapLayer; import org.geotoolkit.ows.xml.GetFeatureInfo; import org.opengis.util.InternationalString; import javax.measure.unit.Unit; import java.awt.*; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; /** * A generic FeatureInfoFormat that produce HTML output for Features and Coverages. * Supported mimeTypes are : * <ul> * <li>text/html</li> * </ul> * * @author Quentin Boileau (Geomatys) * @author Johann Sorel (Geomatys) */ public class HTMLFeatureInfoFormat extends AbstractTextFeatureInfoFormat { private static final class LayerResult{ private String layerName; private final List<String> values = new ArrayList<>(); } private final Map<String,LayerResult> results = new HashMap<>(); public HTMLFeatureInfoFormat() { } /** * {@inheritDoc} */ @Override public List<String> getSupportedMimeTypes() { return Collections.singletonList(MimeType.TEXT_HTML); } /** * {@inheritDoc} */ @Override protected void nextProjectedFeature(ProjectedFeature graphic, RenderingContext2D context, SearchAreaJ2D queryArea) { final FeatureMapLayer layer = graphic.getLayer(); final String layerName = layer.getName(); final Feature feature = graphic.getCandidate(); final FeatureType type = feature.getType(); final Collection<PropertyDescriptor> descs = type.getDescriptors(); LayerResult result = results.get(layerName); if(result==null){ //first feature of this type result = new LayerResult(); result.layerName = layerName; results.put(layerName, result); } //the feature values final StringBuilder typeBuilder = new StringBuilder(); final StringBuilder dataBuilder = new StringBuilder(); typeBuilder.append("<h2>").append(feature.getIdentifier().getID()).append("</h2>"); typeBuilder.append("</br>"); typeBuilder.append("<div>"); typeBuilder.append("<div class=\"left-part\">"); dataBuilder.append("<div class=\"right-part\">"); recursive(feature, typeBuilder, dataBuilder, 0); typeBuilder.append("</div>"); dataBuilder.append("</div>"); dataBuilder.append("</div>"); result.values.add(typeBuilder.toString()); result.values.add(dataBuilder.toString()); } private void recursive(final Property att, final StringBuilder typeBuilder, final StringBuilder dataBuilder, int depth){ if(att instanceof ComplexAttribute){ if(depth!=0){ typeBuilder.append("<li>\n"); typeBuilder.append(att.getDescriptor().getName().tip().toString()); typeBuilder.append("</li>\n"); dataBuilder.append("<br/>\n"); } final ComplexAttribute ca = (ComplexAttribute) att; typeBuilder.append("<ul>\n"); for(Property property : ca.getProperties()){ recursive(property, typeBuilder, dataBuilder , depth+1); } typeBuilder.append("</ul>\n"); }else{ typeBuilder.append("<li>\n"); typeBuilder.append(att.getDescriptor().getName().tip().toString()); typeBuilder.append("</li>\n"); final Object value = att.getValue(); final String valStr = toString(value); dataBuilder.append("<a class=\"values\" title=\""); dataBuilder.append(valStr); dataBuilder.append("\">"); dataBuilder.append(valStr); dataBuilder.append("</a>"); } } private String toString(Object value){ String str; if(value == null){ str = "null"; }else if(value.getClass().isArray()){ //convert to an object array final Object[] array = new Object[Array.getLength(value)]; for(int i=0;i<array.length;i++){ array[i] = toString(Array.get(value, i)); } str = Arrays.toString(array); }else{ str = String.valueOf(value); } return str; } @Override protected void nextProjectedCoverage(ProjectedCoverage graphic, RenderingContext2D context, SearchAreaJ2D queryArea) { final List<Map.Entry<GridSampleDimension,Object>> covResults = FeatureInfoUtilities.getCoverageValues(graphic, context, queryArea); if (covResults == null) { return; } final String layerName = graphic.getLayer().getCoverageReference().getName().tip().toString(); LayerResult result = results.get(layerName); if(result==null){ //first feature of this type result = new LayerResult(); result.layerName = layerName; results.put(layerName, result); } final StringBuilder typeBuilder = new StringBuilder(); final StringBuilder dataBuilder = new StringBuilder(); typeBuilder.append("<div>"); typeBuilder.append("<div class=\"left-part\">"); dataBuilder.append("<div class=\"right-part\">"); typeBuilder.append("<ul>\n"); for(Map.Entry<GridSampleDimension,Object> entry : covResults){ typeBuilder.append("<li>\n"); final GridSampleDimension gsd = entry.getKey(); final InternationalString title = gsd.getDescription(); if(title!=null){ typeBuilder.append(title); } final Unit unit = gsd.getUnits(); if(unit!=null){ typeBuilder.append(unit.toString()); } typeBuilder.append("</li>\n"); dataBuilder.append(String.valueOf(entry.getValue())); dataBuilder.append("<br/>\n"); } typeBuilder.append("</ul>\n"); typeBuilder.append("</div>"); dataBuilder.append("</div>"); dataBuilder.append("</div>"); result.values.add(typeBuilder.toString()); result.values.add(dataBuilder.toString()); } /** * {@inheritDoc} */ @Override public Object getFeatureInfo(final SceneDef sdef, final ViewDef vdef, final CanvasDef cdef, final Rectangle searchArea, final GetFeatureInfo getFI) throws PortrayalException { //fill coverages and features maps getCandidates(sdef, vdef, cdef, searchArea, -1); final StringBuilder response = new StringBuilder(); response.append("<html>\n") .append(" <head>\n") .append(" <title>GetFeatureInfo HTML output</title>\n") .append(" </head>\n") .append(" <style>\n") .append("ul{\n" + " margin-top: 0;\n" + " margin-bottom: 0px;\n" + " }\n" + " .left-part{\n" + " display:inline-block;\n" + " width:350px;\n" + " overflow:auto;\n" + " white-space:nowrap;\n" + " }\n" + " .right-part{\n" + " display:inline-block;\n" + " width:600px;\n" + " overflow: hidden;\n" + " }\n" + " .values{\n" + " text-overflow: ellipsis;\n" + " white-space:nowrap;\n" + " display:block;\n" + " overflow: hidden;\n" + " }") .append(" </style>\n") .append(" <body>\n"); // optimization move this filter to getCandidates Integer maxValue = getFeatureCount(getFI); if (maxValue == null) { maxValue = 1; } for(LayerResult result : results.values()){ response.append("<h2>").append(result.layerName).append("</h2>"); response.append("<br/>"); int cpt = 0; for (final String record : result.values) { response.append(record); cpt++; if (cpt >= maxValue) break; } response.append("<br/>"); } response.append(" </body>\n") .append("</html>"); return response.toString(); } }