Patric Way ha scritto:
Andrea Aime wrote:
María José Pérez ha scritto:
Hello! I cast my project from gt2.1 to 2.3 because I have several
problem with geotools 2.1. Now when I render the map using the class:
"StreamingRenderer", my source code:
panel = new JMapPane();
GTRenderer renderer = new StreamingRenderer();
HashMap hints = new HashMap();
hints.put("memoryPreloadingEnabled", Boolean.TRUE);
renderer.setRendererHints ( hints );
renderer.setContext(compositorMapa.getMap());
panel.setRenderer(renderer);
panel.setContext(TheMap);
panel.setMapArea(TheMap.getAreaOfInterest());
Sigh... the memory preloading was a thing I did develop in a branch
a few years ago to show that streaming rendering could be just as
fast as J2DRenderer if you preloaded in memory and spatially indexed the
geometries.
Unfortunately, that was just a proof of concept and was not intended
to be ported to the production version of geotools, but someone
did port it anyways... Long story short, it works only for a single
layer, because that what I needed for that demo...
Now, fixing IndexedFeatureResults so that it can become a generally
usable class would not be that difficult, but I really have no
time at the moment to work on it. I can try to have a peek at
it during the weekend if you can wait.
Cheers
Andrea
-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
_______________________________________________
Geotools-gt2-users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/geotools-gt2-users
Andrea,
I have a need for a renderer that has memory preloading enabled, I have
reviewed the renderer code a few times and may be able to help. Are you
able to direct my efforts to a specific part of the code? I can afford
to spend between 4 and 8 hours on the project over the next several days.
Cheers,
Patrik, Maria José,
I've developed a little replacement for the memory preloading that I've
attached to this mail.
That's a caching feature source, which is probably more sophisticated
than needed, but I could not resist making it fancy.
That feature source is a caching wrapper against a real feature source.
The first time it gets queried, it loads the query results in a spatial
index, and then uses that index for answering subsequent queries.
Each subsequent query is evaluated, is it's found to be a subset of the
current spatial cache, the cache is used, otherwise the cache is
dropped and rebuilt against the new query.
Being a subset of the query means having the same or less attributes,
and requiring a spatial extend that's a subset of the cached one.
The cache loads only the required attributes to avoid taking too much
room in memory. This plays well with the renderer, that asks only
for the attributes needed for display. Yet, this is at odds with
the hightlight manager used by the mapviewer, that will always ask
for all attributes.
So, if you're using the highlight manager, better prime the cache
with Query.ALL, so that the full dataset, all attributes included,
gets sucked into memory. From that point on, you'll never hit the
disk again, since every query is a subquery of Query.ALL.
(the issue being that the renderer uses only 3 attributes, but
the hightlight manager will asks for all of them, killing the
current cache... maybe some kind of extra logic could be used to
avoid dropping the cache for queries whose bbox is very small,
and hitting the disk for those... would make sense for the
classic "what's here" query).
Anyways, long story short, you can wrap all of you feature sources
inside this caching one, and have all layers kept in memory with
an extra speed boost thanks to the spatial index.
I cooked the class in my spare time without much attention, it
does not have unit tests, but I hope it'll be useful for you anyways.
Let me know how it goes, in my limited tests with the MapViewer
demo variant attached both drawing and highlighting are pretty much
instant (rendering time < 0.3 seeconds).
Cheers
Andrea
package org.geotools.renderer.lite;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.geotools.data.DataSourceException;
import org.geotools.data.DataStore;
import org.geotools.data.DataUtilities;
import org.geotools.data.DefaultQuery;
import org.geotools.data.FeatureListener;
import org.geotools.data.FeatureSource;
import org.geotools.data.Query;
import org.geotools.feature.AttributeType;
import org.geotools.feature.Feature;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureCollections;
import org.geotools.feature.FeatureIterator;
import org.geotools.feature.FeatureType;
import org.geotools.feature.IllegalAttributeException;
import org.geotools.filter.AttributeExpression;
import org.geotools.filter.Expression;
import org.geotools.filter.Filter;
import org.geotools.filter.FilterFactory;
import org.geotools.filter.FilterFactoryFinder;
import org.geotools.filter.GeometryFilter;
import org.geotools.filter.GeometryFilterImpl;
import org.geotools.filter.LiteralExpression;
import org.opengis.filter.And;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.index.SpatialIndex;
import com.vividsolutions.jts.index.strtree.STRtree;
/**
* A caching feature source for fast data access.
*
*/
public class CachingFeatureSource implements FeatureSource {
private FeatureSource wrapped;
private SpatialIndex index;
private boolean dirty;
private Query cachedQuery;
private Envelope cachedBounds;
private FeatureType cachedSchema;
private Envelope originalBounds;
private static FilterFactory ff = FilterFactoryFinder.createFilterFactory();
private static final int[] supportedFilterTypes = new int[] {
Filter.GEOMETRY_BBOX,
Filter.GEOMETRY_CONTAINS, Filter.GEOMETRY_CROSSES,
Filter.GEOMETRY_DWITHIN,
Filter.GEOMETRY_EQUALS, Filter.GEOMETRY_INTERSECTS,
Filter.GEOMETRY_OVERLAPS,
Filter.GEOMETRY_TOUCHES, Filter.GEOMETRY_WITHIN };
static {
Arrays.sort(supportedFilterTypes);
}
public CachingFeatureSource(FeatureSource original) throws IOException {
this.wrapped = original;
this.originalBounds = original.getBounds();
if (originalBounds == null)
originalBounds = new Envelope(-Double.MAX_VALUE, Double.MAX_VALUE,
-Double.MAX_VALUE,
Double.MAX_VALUE);
}
private void fillCache(Query query) throws IOException {
System.out.println("Refilling cache from " + query);
FeatureCollection features = wrapped.getFeatures(query);
FeatureIterator fi = features.features();
index = null;
STRtree newIndex = new STRtree();
while (fi.hasNext()) {
// consider turning all geometries into packed ones, to save space
Feature f = fi.next();
newIndex.insert(f.getBounds(), f);
}
fi.close();
index = newIndex;
cachedQuery = query;
cachedSchema = features.getSchema();
cachedBounds = getEnvelope(query.getFilter());
dirty = false;
}
public void addFeatureListener(FeatureListener listener) {
wrapped.addFeatureListener(listener);
}
public void removeFeatureListener(FeatureListener listener) {
wrapped.removeFeatureListener(listener);
}
public DataStore getDataStore() {
return wrapped.getDataStore();
}
public Envelope getBounds() throws IOException {
return wrapped.getBounds();
}
public Envelope getBounds(Query query) throws IOException {
return wrapped.getBounds(query);
}
public int getCount(Query query) throws IOException {
return wrapped.getCount(query);
}
public FeatureType getSchema() {
return wrapped.getSchema();
}
public FeatureCollection getFeatures() throws IOException {
return getFeatures(Filter.ALL);
}
public FeatureCollection getFeatures(Filter filter) throws IOException {
return getFeatures(new DefaultQuery(wrapped.getSchema().getTypeName(),
filter));
}
public FeatureCollection getFeatures(Query query) throws IOException {
if (query.getTypeName() != null
&&
!wrapped.getSchema().getTypeName().equals(query.getTypeName())) {
throw new DataSourceException("Typename mismatch, query asks for '"
+ query.getTypeName() + " but this feature source provides
'"
+ wrapped.getSchema().getTypeName() + "'");
}
if (index == null || dirty || !isSubQuery(query)) {
fillCache(query);
}
return getFeatureCollection(query, getEnvelope(query.getFilter()));
}
private FeatureCollection getFeatureCollection(Query query, Envelope
bounds) throws IOException {
try {
FeatureType alternate = cachedSchema;
if (query.getPropertyNames() != Query.ALL_NAMES) {
alternate = DataUtilities.createSubType(cachedSchema,
query.getPropertyNames());
if (alternate.equals(cachedSchema))
alternate = cachedSchema;
}
Filter f = query.getFilter();
if (f != null && f.equals(Filter.NONE))
f = null;
List featureList = index.query(bounds);
Feature[] features = (Feature[]) featureList.toArray(new
Feature[featureList.size()]);
FeatureCollection collection = FeatureCollections.newCollection();
for (int i = 0; i < features.length; i++) {
Feature curr = features[i];
if (f != null && !f.contains(curr))
continue;
if (alternate != cachedSchema)
curr = reType(alternate, curr);
collection.add(curr);
}
return collection;
} catch (Exception e) {
throw new DataSourceException(
"Error occurred extracting features from the spatial
index", e);
}
}
/**
* Same as DataUtilities.reType, but without the cloning that uselessly
wastes CPU cycles...
* @param featureType
* @param feature
* @return
* @throws IllegalAttributeException
*/
public static Feature reType(FeatureType featureType, Feature feature)
throws IllegalAttributeException {
FeatureType origional = feature.getFeatureType();
if (featureType.equals(origional)) {
return featureType.duplicate(feature);
}
String id = feature.getID();
int numAtts = featureType.getAttributeCount();
Object[] attributes = new Object[numAtts];
String xpath;
for (int i = 0; i < numAtts; i++) {
AttributeType curAttType = featureType.getAttributeType(i);
xpath = curAttType.getName();
attributes[i] = feature.getAttribute(xpath);
}
return featureType.create(attributes, id);
}
boolean isSubQuery(Query query) {
// no cached data?
if (cachedQuery == null)
return false;
// do we miss some properties?
String[] cachedPropNames = cachedQuery.getPropertyNames();
String[] propNames = query.getPropertyNames();
if (cachedPropNames != Query.ALL_NAMES
&& (propNames == Query.ALL_NAMES ||
!Arrays.asList(cachedPropNames).containsAll(
Arrays.asList(propNames))))
return false;
Filter[] filters = splitFilters(query);
Filter[] cachedFilters = splitFilters(cachedQuery);
if (!filters[0].equals(cachedFilters[0]))
return false;
Envelope envelope = getEnvelope(filters[1]);
return cachedBounds.contains(envelope);
}
Envelope getEnvelope(Filter filter) {
Envelope result = originalBounds;
if (filter instanceof And) {
Envelope bounds = new Envelope();
for (Iterator iter = ((And) filter).getChildren().iterator();
iter.hasNext();) {
Filter f = (Filter) iter.next();
Envelope e = getEnvelope(f);
if (e == null)
return null;
else
bounds.expandToInclude(e);
}
result = bounds;
} else if (filter instanceof GeometryFilter) {
GeometryFilter gf = (GeometryFilter) filter;
if (Arrays.binarySearch(supportedFilterTypes, gf.getFilterType()) <
0) {
Expression lg = gf.getLeftGeometry();
Expression rg = gf.getRightGeometry();
if (lg instanceof LiteralExpression) {
Geometry g = (Geometry) ((LiteralExpression) lg).getValue();
if (rg instanceof AttributeExpression)
result = g.getEnvelopeInternal();
} else if (rg instanceof LiteralExpression) {
Geometry g = (Geometry) ((LiteralExpression) rg).getValue();
if (lg instanceof AttributeExpression)
result = g.getEnvelopeInternal();
}
}
}
return result.intersection(originalBounds);
}
/**
* Splits a query into two parts, a spatial component that can be turned
into a bbox filter (by
* including some more feature in the result) and a residual component that
we cannot address
* with the spatial index
*
* @param query
*/
Filter[] splitFilters(Query query) {
Filter filter = query.getFilter();
if (filter == null || filter.equals(Filter.NONE)) {
return new Filter[] { Filter.NONE, bboxFilter(originalBounds) };
}
if (!(filter instanceof And)) {
Envelope envelope = getEnvelope(filter);
if (envelope == null)
return new Filter[] { Filter.NONE, bboxFilter(originalBounds) };
else
return new Filter[] { Filter.NONE, bboxFilter(envelope) };
}
And and = (And) filter;
List residuals = new ArrayList();
List bboxBacked = new ArrayList();
for (Iterator it = and.getChildren().iterator(); it.hasNext();) {
Filter child = (Filter) it.next();
if (getEnvelope(child) != null) {
bboxBacked.add(child);
} else {
residuals.add(child);
}
}
return new Filter[] { (Filter) ff.and(residuals), (Filter)
ff.and(bboxBacked) };
}
private GeometryFilterImpl bboxFilter(Envelope bbox) {
GeometryFilterImpl gf = (GeometryFilterImpl) ff
.createGeometryFilter(GeometryFilter.GEOMETRY_BBOX);
gf.setExpression1(ff.createAttributeExpression(wrapped.getSchema().getDefaultGeometry()));
gf.setExpression2(ff.createBBoxExpression(bbox));
return gf;
}
}
/*
* GeoTools - OpenSource mapping toolkit
* http://geotools.org
* (C) 2006, GeoTools Project Managment Committee (PMC)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library 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
* Lesser General Public License for more details.
*/
package org.geotools.demo.mappane;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JToolBar;
import javax.swing.WindowConstants;
import org.geotools.data.FeatureSource;
import org.geotools.data.Query;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.gui.swing.JMapPane;
import org.geotools.gui.swing.PanAction;
import org.geotools.gui.swing.ResetAction;
import org.geotools.gui.swing.SelectAction;
import org.geotools.gui.swing.ZoomInAction;
import org.geotools.gui.swing.ZoomOutAction;
import org.geotools.map.DefaultMapContext;
import org.geotools.map.MapContext;
import org.geotools.referencing.CRS;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.renderer.GTRenderer;
import org.geotools.renderer.lite.CachingFeatureSource;
import org.geotools.renderer.lite.StreamingRenderer;
import org.geotools.styling.SLDParser;
import org.geotools.styling.StyleFactory;
import org.geotools.styling.StyleFactoryFinder;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
/**
* Sample application that may be used to try JMapPane from the command line.
*
* @author Ian Turton
*/
public class MapViewer2 {
JFrame frame;
JMapPane mp;
JToolBar jtb;
JLabel text;
public MapViewer2(){
frame=new JFrame("My Map Viewer");
frame.setBounds(20,20,450,200);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
Container content = frame.getContentPane();
mp = new JMapPane();
//mp.addZoomChangeListener(this);
content.setLayout(new BorderLayout());
jtb = new JToolBar();
Action zoomIn = new ZoomInAction(mp);
Action zoomOut = new ZoomOutAction(mp);
Action pan = new PanAction(mp);
Action select = new SelectAction(mp);
Action reset = new ResetAction(mp);
jtb.add(zoomIn);
jtb.add(zoomOut);
jtb.add(pan);
jtb.addSeparator();
jtb.add(reset);
jtb.addSeparator();
jtb.add(select);
final JButton button= new JButton();
button.setText("CRS");
button.setToolTipText("Change map prjection");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String code = JOptionPane.showInputDialog( button, "Coordinate
Reference System:", "EPSG:4326" );
try{
CoordinateReferenceSystem crs = CRS.decode( code );
setCRS( crs );
}
catch(FactoryException fe){
JOptionPane.showMessageDialog( button, fe.getMessage(),
fe.getClass().toString(), JOptionPane.ERROR_MESSAGE );
return;
}
}
});
jtb.add(button);
content.add(jtb,BorderLayout.NORTH);
//JComponent sp = mp.createScrollPane();
mp.setSize(400,200);
content.add(mp,BorderLayout.CENTER);
content.doLayout();
frame.setVisible(true);
}
/**
* Method used to set the current map projection.
*
* @param crs A new CRS for the mappnae.
*/
public void setCRS(CoordinateReferenceSystem crs){
mp.getContext().setAreaOfInterest(mp.getContext().getAreaOfInterest(),crs);
mp.setReset(true);
mp.repaint();
}
public void load(URL shape, URL sld)throws Exception{
ShapefileDataStore ds = new ShapefileDataStore(shape);
FeatureSource fs = ds.getFeatureSource();
fs = new CachingFeatureSource(fs);
// fs.getFeatures(Query.ALL);
com.vividsolutions.jts.geom.Envelope env = fs.getBounds();
mp.setMapArea(env);
StyleFactory factory = StyleFactoryFinder.createStyleFactory();
SLDParser stylereader = new SLDParser(factory,sld);
org.geotools.styling.Style[] style = stylereader.readXML();
MapContext context = new DefaultMapContext(DefaultGeographicCRS.WGS84);
context.addLayer(fs,style[0]);
context.getLayerBounds();
// mp.setHighlightLayer(context.getLayer(0));
GTRenderer renderer;
renderer = new StreamingRenderer();
HashMap hints = new HashMap();
hints.put("optimizedDataLoadingEnabled", Boolean.TRUE);
renderer.setRendererHints( hints );
mp.setRenderer(renderer);
mp.setContext(context);
mp.setBackground(Color.WHITE);
frame.repaint();
frame.doLayout();
}
public static URL aquireURL( String target ){
if( new File( target ).exists() ){
try {
return new File( target ).toURL();
} catch (MalformedURLException e) {
}
}
try {
return new URL( target );
} catch (MalformedURLException e) {
return null;
}
}
/**
* @param args
*/
public static void main(String[] args) throws Exception {
if( args.length==0 || !args[0].toLowerCase().endsWith(".shp")){
System.out.println("java org.geotools.gui.swing.MapViewer
shapefile.shp");
System.out.println("Notes:");
System.out.println(" Any provided shapefile.prj file or
shapefile.sld will be used");
System.exit(0);
}
String pathname = args[0];
URL shape = aquireURL( pathname );
if( shape == null ){
System.err.println("Could not find shapefile: "+pathname);
System.exit(1);
}
String filepart = pathname.substring(0, pathname.lastIndexOf("."));
URL sld = aquireURL( filepart+".sld" );
if( sld == null){
System.err.println("Could not find sld file: "+filepart+".sld");
System.exit(1);
}
MapViewer2 mapV = new MapViewer2();
mapV.load( shape, sld );
}
}
-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
_______________________________________________
Geotools-gt2-users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/geotools-gt2-users