Skip to content

Commit

Permalink
Add buffer to vector tiles so wide symbols are not clipped at tile bo…
Browse files Browse the repository at this point in the history
…undaries
  • Loading branch information
smithkm committed Jun 13, 2017
1 parent 8d09c28 commit 43c8a18
Show file tree
Hide file tree
Showing 4 changed files with 281 additions and 87 deletions.
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ static class Context {


public double pixelSizeInTargetCRS; // approximate size of a pixel in the Target CRS public double pixelSizeInTargetCRS; // approximate size of a pixel in the Target CRS


public int queryBuffer;
} }


Context context; Context context;
Expand All @@ -98,20 +99,24 @@ private PipelineBuilder(Context context) {
* @throws FactoryException * @throws FactoryException
*/ */
public static PipelineBuilder newBuilder(ReferencedEnvelope renderingArea, Rectangle paintArea, public static PipelineBuilder newBuilder(ReferencedEnvelope renderingArea, Rectangle paintArea,
CoordinateReferenceSystem sourceCrs, double overSampleFactor) throws FactoryException { CoordinateReferenceSystem sourceCrs, double overSampleFactor, int queryBuffer)
throws FactoryException {


Context context = createContext(renderingArea, paintArea, sourceCrs, overSampleFactor); Context context = createContext(renderingArea, paintArea, sourceCrs, overSampleFactor,
queryBuffer);
return new PipelineBuilder(context); return new PipelineBuilder(context);
} }


private static Context createContext(ReferencedEnvelope mapArea, Rectangle paintArea, private static Context createContext(ReferencedEnvelope mapArea, Rectangle paintArea,
CoordinateReferenceSystem sourceCrs, double overSampleFactor) throws FactoryException { CoordinateReferenceSystem sourceCrs, double overSampleFactor, int queryBuffer)
throws FactoryException {


Context context = new Context(); Context context = new Context();
context.renderingArea = mapArea; context.renderingArea = mapArea;
context.paintArea = paintArea; context.paintArea = paintArea;
context.sourceCrs = sourceCrs; context.sourceCrs = sourceCrs;
context.worldToScreen = RendererUtilities.worldToScreenTransform(mapArea, paintArea); context.worldToScreen = RendererUtilities.worldToScreenTransform(mapArea, paintArea);
context.queryBuffer = queryBuffer;


final boolean wrap = false; final boolean wrap = false;
context.projectionHandler = ProjectionHandlerFinder.getHandler(mapArea, sourceCrs, wrap); context.projectionHandler = ProjectionHandlerFinder.getHandler(mapArea, sourceCrs, wrap);
Expand Down Expand Up @@ -288,12 +293,12 @@ public PipelineBuilder clip(boolean clipToMapBounds, boolean transformToScreenCo
Rectangle screen = context.paintArea; Rectangle screen = context.paintArea;


Envelope paintArea = new Envelope(0, screen.getWidth(), 0, screen.getHeight()); Envelope paintArea = new Envelope(0, screen.getWidth(), 0, screen.getHeight());
paintArea.expandBy(clipBBOXSizeIncreasePixels); paintArea.expandBy(clipBBOXSizeIncreasePixels+context.queryBuffer);


clippingEnvelope = paintArea; clippingEnvelope = paintArea;
} else { } else {
ReferencedEnvelope renderingArea = context.renderingArea; ReferencedEnvelope renderingArea = context.renderingArea;
renderingArea.expandBy(clipBBOXSizeIncreasePixels * context.pixelSizeInTargetCRS); renderingArea.expandBy((clipBBOXSizeIncreasePixels+context.queryBuffer) * context.pixelSizeInTargetCRS);
clippingEnvelope = renderingArea; clippingEnvelope = renderingArea;
} }


Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -28,12 +28,14 @@
import org.geotools.feature.FeatureIterator; import org.geotools.feature.FeatureIterator;
import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.map.Layer; import org.geotools.map.Layer;
import org.geotools.renderer.lite.VectorMapRenderUtils;
import org.geotools.util.logging.Logging; import org.geotools.util.logging.Logging;
import org.opengis.feature.Attribute; import org.opengis.feature.Attribute;
import org.opengis.feature.ComplexAttribute; import org.opengis.feature.ComplexAttribute;
import org.opengis.feature.Feature; import org.opengis.feature.Feature;
import org.opengis.feature.GeometryAttribute; import org.opengis.feature.GeometryAttribute;
import org.opengis.feature.Property; import org.opengis.feature.Property;
import org.opengis.feature.type.FeatureType;
import org.opengis.feature.type.GeometryDescriptor; import org.opengis.feature.type.GeometryDescriptor;
import org.opengis.referencing.FactoryException; import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.crs.CoordinateReferenceSystem;
Expand Down Expand Up @@ -114,71 +116,42 @@ public WebMap produceMap(final WMSMapContent mapContent) throws ServiceException
} }


sourceCrs = geometryDescriptor.getType().getCoordinateReferenceSystem(); sourceCrs = geometryDescriptor.getType().getCoordinateReferenceSystem();

int buffer = VectorMapRenderUtils.getComputedBuffer(mapContent.getBuffer(),
PipelineBuilder builder; VectorMapRenderUtils.getFeatureStyles(layer, paintArea,
try { VectorMapRenderUtils.getMapScale(mapContent, renderingArea),
builder = PipelineBuilder.newBuilder(renderingArea, paintArea, sourceCrs, (FeatureType)featureSource.getSchema()));
overSamplingFactor); Pipeline pipeline = getPipeline(mapContent, renderingArea, paintArea, sourceCrs, buffer);
} catch (FactoryException e) {
throw new ServiceException(e);
}

Pipeline pipeline = builder.preprocess().transform(transformToScreenCoordinates)
.simplify(transformToScreenCoordinates)
.clip(clipToMapBounds, transformToScreenCoordinates).collapseCollections()
.build();

Query query = getStyleQuery(layer, mapContent); Query query = getStyleQuery(layer, mapContent);
query.getHints().remove(Hints.SCREENMAP); query.getHints().remove(Hints.SCREENMAP);


FeatureCollection<?, ?> features = featureSource.getFeatures(query); FeatureCollection<?, ?> features = featureSource.getFeatures(query);
Feature feature;
Stopwatch sw = Stopwatch.createStarted(); run(features, pipeline, geometryDescriptor, vectorTileBuilder, layer);
int count = 0;
int total = 0;

try (FeatureIterator<?> it = features.features()) {
while (it.hasNext()) {
feature = it.next();
total++;
Geometry originalGeom;
Geometry finalGeom;

originalGeom = (Geometry) feature.getDefaultGeometryProperty().getValue();
try {
finalGeom = pipeline.execute(originalGeom);
} catch (Exception processingException) {
processingException.printStackTrace();
continue;
}
if (finalGeom.isEmpty()) {
continue;
}

final String layerName = feature.getName().getLocalPart();
final String featureId = feature.getIdentifier().toString();
final String geometryName = geometryDescriptor.getName().getLocalPart();

final Map<String, Object> properties = getProperties(feature);

vectorTileBuilder.addFeature(layerName, featureId, geometryName, finalGeom,
properties);
count++;
}
}
sw.stop();
if (LOGGER.isLoggable(Level.FINE)) {
String msg = String.format("Added %,d out of %,d features of '%s' in %s", count,
total, layer.getTitle(), sw);
// System.err.println(msg);
LOGGER.fine(msg);
}
} }

WebMap map = vectorTileBuilder.build(mapContent); WebMap map = vectorTileBuilder.build(mapContent);
return map; return map;
} }


protected Pipeline getPipeline(final WMSMapContent mapContent,
final ReferencedEnvelope renderingArea, final Rectangle paintArea,
CoordinateReferenceSystem sourceCrs, int buffer) {
Pipeline pipeline;
try {
final PipelineBuilder builder = PipelineBuilder.newBuilder(renderingArea, paintArea, sourceCrs,
overSamplingFactor, buffer);

pipeline = builder.preprocess().transform(transformToScreenCoordinates)
.simplify(transformToScreenCoordinates)
.clip(clipToMapBounds, transformToScreenCoordinates).collapseCollections()
.build();
} catch (FactoryException e) {
throw new ServiceException(e);
}
return pipeline;
}

private Map<String, Object> getProperties(ComplexAttribute feature) { private Map<String, Object> getProperties(ComplexAttribute feature) {
Map<String, Object> props = new TreeMap<>(); Map<String, Object> props = new TreeMap<>();
for (Property p : feature.getProperties()) { for (Property p : feature.getProperties()) {
Expand All @@ -199,6 +172,52 @@ private Map<String, Object> getProperties(ComplexAttribute feature) {
return props; return props;
} }


void run(FeatureCollection<?, ?> features, Pipeline pipeline,
GeometryDescriptor geometryDescriptor,
VectorTileBuilder vectorTileBuilder, Layer layer){
Stopwatch sw = Stopwatch.createStarted();
int count = 0;
int total = 0;
Feature feature;

try (FeatureIterator<?> it = features.features()) {
while (it.hasNext()) {
feature = it.next();
total++;
Geometry originalGeom;
Geometry finalGeom;

originalGeom = (Geometry) feature.getDefaultGeometryProperty().getValue();
try {
finalGeom = pipeline.execute(originalGeom);
} catch (Exception processingException) {
processingException.printStackTrace();
continue;
}
if (finalGeom.isEmpty()) {
continue;
}

final String layerName = feature.getName().getLocalPart();
final String featureId = feature.getIdentifier().toString();
final String geometryName = geometryDescriptor.getName().getLocalPart();

final Map<String, Object> properties = getProperties(feature);

vectorTileBuilder.addFeature(layerName, featureId, geometryName, finalGeom,
properties);
count++;
}
}
sw.stop();
if (LOGGER.isLoggable(Level.FINE)) {
String msg = String.format("Added %,d out of %,d features of '%s' in %s", count,
total, layer.getTitle(), sw);
// System.err.println(msg);
LOGGER.fine(msg);
}
}

/** /**
* @return {@code null}, not a raster format. * @return {@code null}, not a raster format.
*/ */
Expand Down

0 comments on commit 43c8a18

Please sign in to comment.