Benutzer:Alexrk2/Geotools
Geotools in der Kartenwerkstatt
[Bearbeiten | Quelltext bearbeiten ]Geotools ist eine (mittlerweile sehr umfangreiche) freie Java-Bibliothek zum Manipulieren, Verarbeiten und Darstellen von Geodaten. Für die Kartenherstellung ist Geotools insbesondere mit seinen Fähigkeiten interessant, aus diversen Geodatenformaten Karten in PNG oder auch SVG zu zeichnen.
Ein ganz einfaches Beispiel
[Bearbeiten | Quelltext bearbeiten ]Als Quelldaten haben wir eine Shape-Datei (zB aus der TIGER-Datenbank der US-Census-Behörde) und möchte diese in eine SVG-Datei überführen.
Der folgende Code erstellt ein MapContext-Objekt mit einem Layer aus den Daten der Shape-Datei. Als Kartenprojektion wird hier die "North America Lambert Conformal Conic" gewählt. Der Layer bekommt noch einen Symbolizer als Darstellungsmerkmal (hier einfach: Farbe Schwarz und 2px Breite).
// Aus einem Shapefile wird zunächst ein Datastore geöffnet FileshapeFile=newFile("tl_2008_us_state.shp"); ShapefileDataStoredataStore=newShapefileDataStore(shapeFile.toURI().toURL()); List<MapLayer>layers=newArrayList<MapLayer>(); // .. mit dem Datastore wird ein Layer erstellt (mitsamt grafischer Ausprägung) StyleBuildersb=newStyleBuilder(); Symbolizersym=sb.createLineSymbolizer(Color.BLACK,2); layers.add(newDefaultMapLayer(dataStore.getFeatureSource(),sb.createStyle(sym))); // Erstelle eine Karte aus 1 Layer und Referenzsystem "North America Lambert Conformal Conic" MapContextmapContext=newDefaultMapContext(layers.toArray(newMapLayer[]{}) ,CRS.decode("EPSG:102009")); // Es folgt das Erstellen der SVG-Datei DocumentBuilderFactorydbf=DocumentBuilderFactory.newInstance(); DocumentBuilderdb=dbf.newDocumentBuilder(); Documentdocument=db.getDOMImplementation().createDocument( "http://www.w3.org/2000/svg","svg",null); SVGGeneratorContextcontext=SVGGeneratorContext.createDefault(document); SVGGraphics2Dg=newSVGGraphics2D(context,true); g.setSVGCanvasSize(newDimension(900,500)); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); Rectanglerect=newRectangle(g.getSVGCanvasSize()); // Der Renderer übernimmt schliesslich das "Zeichnen" der Karte, // in dem Fall direkt in die SVG-Datei rein StreamingRendererrenderer=newStreamingRenderer(); renderer.setContext(mapContext); MaprendererParams=newHashMap(); rendererParams.put("optimizedDataLoadingEnabled",Boolean.TRUE); rendererParams.put(StreamingRenderer.OPTIMIZE_FTS_RENDERING_KEY,Boolean.TRUE); rendererParams.put(StreamingRenderer.LINE_WIDTH_OPTIMIZATION_KEY,Boolean.FALSE); renderer.setRendererHints(rendererParams); // Als Parameter werden hier noch übergeben: // paintArea: Ausgabebereich der SVG-Seite // mapArea: Eine Boundingbox mit Kartenkoordinaten renderer.paint(g,rect,mapContext.getAreaOfInterest()); g.stream("test.svg");
Das Resultat dieses Beispiels sieht dann wie folgt aus:
Darstellungsregeln mit dem Symbolizer
[Bearbeiten | Quelltext bearbeiten ]Als nächstes soll mit Hilfe des Symbolizers abhängig von einer Filterregel eine abweichende Darstellung gewählt werden. Im folgenden Beispiel also: wenn NAME=Texas, dann andere Farbe
StyleBuildersb=newStyleBuilder(); Symbolizersym=sb.createPolygonSymbolizer(newColor(246,180,160)); Rulerule1=sb.createRule(sym); rule1.setFilter(CQL.toFilter("NAME = 'Texas'")); Symbolizersym2=sb.createPolygonSymbolizer(newColor(200,200,200)); Rulerule2=sb.createRule(sym2); rule2.setIsElseFilter(true); FeatureTypeStylefts=sb.createFeatureTypeStyle("Feature",newRule[]{rule1,rule2}); Stylestyle=sb.createStyle(); style.addFeatureTypeStyle(fts); layers.add(newDefaultMapLayer(dataStore.getFeatureSource(),style)); sym=sb.createLineSymbolizer(Color.BLACK,1);
Am oberen Beispiel erkennt man auch ein kleines Manko des SVG-Renderers von Geotools: nämlich dass man entweder nur Linien oder nur Flächen in einem Layer zeichnen kann - jedoch keine gefüllten Objekte mit Umrandund. Dazu muss man 2 separate Layer übereinander legen. Der Grund dieses Mankos liegt in der Grafik-Klasse von Java, die dies nicht unterstützt. Eine andere Alternative wäre: man lässt den Renderer nur die Flächen zeichnen und setzt die Umrandung später selbst (zB in einem SVG-Editor wie Inkscape).
Den Kartenausschnitt wählen (Bounding Box)
[Bearbeiten | Quelltext bearbeiten ]Im oberen Beispiel wurde mittels mapContext.getAreaOfInterest() einfach der gesamte Kartenbereich exportiert. Möchte man aber zB nur den Staat Vermont exportieren, so kann man zB die Bounding Box des Staates Vermonts heraussuchen und diese dann als Paramter beim Export angeben.
ReferencedEnvelopebbox=null; FeatureCollection<SimpleFeatureType,SimpleFeature>collection=dataStore.getFeatureSource().getFeatures(); FeatureIterator<SimpleFeature>iterator=collection.features(); while(iterator.hasNext()){ SimpleFeaturefeature=iterator.next(); Stringstate=(String)feature.getProperty("NAME").getValue(); // Nach einem Feature namens "Vermont" suchen if(state.equalsIgnoreCase("Vermont")){ bbox=(ReferencedEnvelope)feature.getBounds(); // und noch um 0.2° erweitern bbox.expandBy(0.2); } } // Wichtig: die BBOX muss noch in das Referenzsystem der Karte umgerechnet werden bbox=bbox.transform(mapContext.getCoordinateReferenceSystem(),true);
Der nächste Schritt beschäftigt sich mit dem Berechnen der Höhe und Breite (entspr. des Ausgangsseitenverhältnisses)
doublewidth=-1; doubleheight=-1; if((bbox.getHeight()>0)&&(bbox.getWidth()>0)){ if(bbox.getHeight()>=bbox.getWidth()){ height=500; width=height*(bbox.getWidth()/bbox.getHeight()); }else{ width=500; height=width*(bbox.getHeight()/bbox.getWidth()); }
Anschließend geht es dann wieder zum Rendern. Doch diesmal wird vorher noch ein Clipping-Rechteck (also ein Ausschneidepfad) gesetzt, da sonst einige Teile der Karte über den Seitenrand der SVG-Datei ragen.
g.setSVGCanvasSize(newDimension((int)width,(int)height)); ... g.setClip(0,0,(int)width,(int)height); renderer.paint(g,rect,bbox);