Geotools GTRender 创建了位置错误的瓦片。

huangapple go评论59阅读模式
英文:

Geotools GTRender creates misplaced tiles

问题

以下是您要翻译的代码部分:

if have put together a tilerenderer based on the GTRenderer from the geotools Framework.

The Problem is that these Tiles contain an height offset (See picture).

[![enter image description here][1]][1]

[1]: https://i.stack.imgur.com/IlcVe.png

This is my code:

import org.geotools.data.DataStore;
import org.geotools.data.DataStore;
import org.geotools.data.DataStoreFinder;
import org.geotools.data.FeatureSource;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.geometry.jts.JTS;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.map.FeatureLayer;
import org.geotools.map.Layer;
import org.geotools.map.MapContent;
import org.geotools.map.MapViewport;
import org.geotools.referencing.CRS;
import org.geotools.referencing.wkt.Parser;
import org.geotools.renderer.GTRenderer;
import org.geotools.renderer.lite.StreamingRenderer;
import org.geotools.styling.*;
import org.geotools.xml.styling.SLDParser;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.Geometry;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory2;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.imageio.ImageIO;
import javax.swing.plaf.synth.ColorType;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.HashMap;

import java.util.Map;
/**
 * This Renderer uses the geotools library to render the world shape and city names as tile png image.
 * */
@Component
public class WorldRasterTileRenderer {
    private static final Logger logger = LoggerFactory.getLogger(WorldRasterTileRenderer.class);
    private MapContent map;
    private GTRenderer renderer;
    private Rectangle tilePixelSize = new Rectangle(0, 0, 256, 256);
    private CoordinateReferenceSystem crs;

    @PostConstruct
    public void init(){
        try {
            map = createMapContent();
            renderer = new StreamingRenderer();
            renderer.setMapContent(map);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    private MapContent createMapContent() throws Exception {
        MapContent map = new MapContent();
        map.setTitle("WorldMap");
		//create country layer
        FeatureSource<SimpleFeatureType, SimpleFeature> featureSourceCountry = readShapefile("/country_lines.shp");
        Style style = createStyle(featureSourceCountry);
        Layer countryLayer = new FeatureLayer(featureSourceCountry, style,"country");
        map.addLayer(countryLayer);
        
		MapViewport mapViewport = new MapViewport();
        //mapViewport.setCoordinateReferenceSystem(crs);
        map.setViewport(mapViewport);

        return map;
    }
    private FeatureSource<SimpleFeatureType, SimpleFeature> readShapefile(String path) throws IOException, FactoryException {
        File file = new File(path);
        Map<String, Object> filemap = new HashMap<>();
        filemap.put("url", file.toURI().toURL());

        DataStore dataStore = DataStoreFinder.getDataStore(filemap);
        String typeName = dataStore.getTypeNames()[0];
        FeatureSource<SimpleFeatureType, SimpleFeature> source = dataStore.getFeatureSource(typeName);
        SimpleFeatureType schema = source.getSchema();

        return source;
    }

    public synchronized byte[] renderRasterTile(int x, int y, int z){
        Envelope tileBounds = this.getTileBounds(x,y,z);

        BufferedImage image = new BufferedImage(tilePixelSize.width, tilePixelSize.height, BufferedImage.TYPE_INT_RGB);

        Graphics2D gr = image.createGraphics();
        gr.setPaint(Color.decode("#b8dee6"));
        gr.fill(tilePixelSize);

        try {
            renderer.paint(gr, tilePixelSize, tileBounds);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ImageIO.write( image, "png", baos );
            baos.flush();
            byte[] imageInByte = baos.toByteArray();
            baos.close();
            return imageInByte;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    private Style loadStyleFromXml() throws Exception {
        java.net.URL base = getClass().getResource("rs-testData");

        StyleFactory factory = CommonFactoryFinder.getStyleFactory();
        java.net.URL surl = new java.net.URL(base + "/markTest.sld");

        SLDParser stylereader = new SLDParser( factory, surl);
        Style styles[] = stylereader.readXML();
        return styles[0];
    }
    public Envelope getTileBounds(int x, int y, int zoom)
    {
        return new Envelope(this.getLong(x, zoom), this.getLong(x + 1, zoom), this.getLat(y,zoom), this.getLat(y+1, zoom));   
    }

    public double getLong(int x, int zoom)
    {
        return ( x / Math.pow(2, zoom) * 360 - 180 );
    }

    public double getLat(int y, int zoom)
    {
        double r2d = 180 / Math.PI;
        double n = Math.PI - 2 * Math.PI * y / Math.pow(2, zoom);
        return r2d * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)));
    }
}

请注意,由于代码中存在一些HTML实体(如&quot;),您可能需要将它们替换为相应的字符,以确保代码的正确性。如果您有其他翻译或问题,请告诉我。

英文:

if have put together a tilerenderer based on the GTRenderer from the geotools Framework.

The Problem is that these Tiles contain an height offset (See picture).

Geotools GTRender 创建了位置错误的瓦片。

This is my code:

   import org.geotools.data.DataStore;
import org.geotools.data.DataStore;
import org.geotools.data.DataStoreFinder;
import org.geotools.data.FeatureSource;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.geometry.jts.JTS;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.map.FeatureLayer;
import org.geotools.map.Layer;
import org.geotools.map.MapContent;
import org.geotools.map.MapViewport;
import org.geotools.referencing.CRS;
import org.geotools.referencing.wkt.Parser;
import org.geotools.renderer.GTRenderer;
import org.geotools.renderer.lite.StreamingRenderer;
import org.geotools.styling.*;
import org.geotools.xml.styling.SLDParser;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.Geometry;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory2;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.imageio.ImageIO;
import javax.swing.plaf.synth.ColorType;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.Map;
/**
* This Renderer uses the geotools library to render the world shape and city names as tile png image.
* */
@Component
public class WorldRasterTileRenderer {
private static final Logger logger = LoggerFactory.getLogger(WorldRasterTileRenderer.class);
private MapContent map;
private GTRenderer renderer;
private Rectangle tilePixelSize = new Rectangle(0, 0, 256, 256);
private CoordinateReferenceSystem crs;
@PostConstruct
public void init(){
try {
map = createMapContent();
renderer = new StreamingRenderer();
renderer.setMapContent(map);
}catch (Exception e){
e.printStackTrace();
}
}
private MapContent createMapContent() throws Exception {
MapContent map = new MapContent();
map.setTitle(&quot;WorldMap&quot;);
//create country layer
FeatureSource&lt;SimpleFeatureType, SimpleFeature&gt; featureSourceCountry = readShapefile(&quot;/country_lines.shp&quot;);
Style style = createStyle(featureSourceCountry);
Layer countryLayer = new FeatureLayer(featureSourceCountry, style,&quot;country&quot;);
map.addLayer(countryLayer);
MapViewport mapViewport = new MapViewport();
//mapViewport.setCoordinateReferenceSystem(crs);
map.setViewport(mapViewport);
return map;
}
private FeatureSource&lt;SimpleFeatureType, SimpleFeature&gt; readShapefile(String path) throws IOException, FactoryException {
File file = new File(path);
Map&lt;String, Object&gt; filemap = new HashMap&lt;&gt;();
filemap.put(&quot;url&quot;, file.toURI().toURL());
DataStore dataStore = DataStoreFinder.getDataStore(filemap);
String typeName = dataStore.getTypeNames()[0];
FeatureSource&lt;SimpleFeatureType, SimpleFeature&gt; source = dataStore.getFeatureSource(typeName);
SimpleFeatureType schema = source.getSchema();
return source;
}
public synchronized byte[] renderRasterTile(int x, int y, int z){
Envelope tileBounds = this.getTileBounds(x,y,z);
BufferedImage image = new BufferedImage(tilePixelSize.width, tilePixelSize.height, BufferedImage.TYPE_INT_RGB);
Graphics2D gr = image.createGraphics();
gr.setPaint(Color.decode(&quot;#b8dee6&quot;));
gr.fill(tilePixelSize);
try {
renderer.paint(gr, tilePixelSize, tileBounds);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write( image, &quot;png&quot;, baos );
baos.flush();
byte[] imageInByte = baos.toByteArray();
baos.close();
return imageInByte;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private Style loadStyleFromXml() throws Exception {
java.net.URL base = getClass().getResource(&quot;rs-testData&quot;);
StyleFactory factory = CommonFactoryFinder.getStyleFactory();
java.net.URL surl = new java.net.URL(base + &quot;/markTest.sld&quot;);
SLDParser stylereader = new SLDParser( factory, surl);
Style styles[] = stylereader.readXML();
return styles[0];
}
public Envelope getTileBounds(int x, int y, int zoom)
{
return new Envelope(this.getLong(x, zoom), this.getLong(x + 1, zoom), this.getLat(y,zoom), this.getLat(y+1, zoom));   
}
public double getLong(int x, int zoom)
{
return ( x / Math.pow(2, zoom) * 360 - 180 );
}
public double getLat(int y, int zoom)
{
double r2d = 180 / Math.PI;
double n = Math.PI - 2 * Math.PI * y / Math.pow(2, zoom);
return r2d * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)));
}
}

In the code all Projections should be WGS84.

It has to be some kind of Projection issue it believe, but having the shape source in EPSG:3857 does lead to emtpy tiles. Even if I change the MapViewport to the same projection.

Do you have an idea what I might be missing ?

答案1

得分: 1

我无法确定具体问题在哪里,但是你计算瓦片的纬度和经度的方式看起来与“gt-tile-client”代码中OSM瓦片的计算有些不同:

public Tile findTileAtCoordinate(double lon, double lat, ZoomLevel zoomLevel,
        TileService service) {
    lat = TileFactory.normalizeDegreeValue(lat, 90);
    lon = TileFactory.normalizeDegreeValue(lon, 180);

    /**
     * 因为纬度仅在85.0511°N至85.0511°S之间有效(http://wiki.openstreetmap.org/wiki/Tilenames#X_and_Y),所以我们必须校正
     * 如果有必要的话。
     */
    lat = OSMTileFactory.moveInRange(lat, WebMercatorTileService.MIN_LATITUDE,
            WebMercatorTileService.MAX_LATITUDE);

    int xTile = (int) Math.floor((lon + 180) / 360 * (1 << zoomLevel.getZoomLevel()));
    int yTile = (int) Math.floor(
            (1 - Math.log(Math.tan(lat * Math.PI / 180) + 1 / Math.cos(lat * Math.PI / 180))
                    / Math.PI) / 2 * (1 << zoomLevel.getZoomLevel()));
    if (yTile < 0)
        yTile = 0;
    return new OSMTile(xTile, yTile, zoomLevel, service);
}

[1]: https://github.com/geotools/geotools/blob/master/modules/extension/tile-client/src/main/java/org/geotools/tile/impl/osm/OSMTileFactory.java#L51

注意:我没有翻译代码中的HTML实体,如&lt;&gt;,因为它们是用于HTML的特殊字符,而在代码中应该直接使用小于号<和大于号>

英文:

I can't tell exactly what's wrong but the way you calculate the lat, lon of a tile looks a little off when I compare it to the calculation in the gt-tile-client code for OSM tiles:

public Tile findTileAtCoordinate(double lon, double lat, ZoomLevel zoomLevel,
TileService service) {
lat = TileFactory.normalizeDegreeValue(lat, 90);
lon = TileFactory.normalizeDegreeValue(lon, 180);
/**
* Because the latitude is only valid in 85.0511 &#176;N to 85.0511 &#176;S (http://wiki.openstreetmap.org/wiki/Tilenames#X_and_Y), we have to correct
* if necessary.
*/
lat = OSMTileFactory.moveInRange(lat, WebMercatorTileService.MIN_LATITUDE,
WebMercatorTileService.MAX_LATITUDE);
int xTile = (int) Math.floor((lon + 180) / 360 * (1 &lt;&lt; zoomLevel.getZoomLevel()));
int yTile = (int) Math.floor(
(1 - Math.log(Math.tan(lat * Math.PI / 180) + 1 / Math.cos(lat * Math.PI / 180))
/ Math.PI) / 2 * (1 &lt;&lt; zoomLevel.getZoomLevel()));
if (yTile &lt; 0)
yTile = 0;
return new OSMTile(xTile, yTile, zoomLevel, service);
}

huangapple
  • 本文由 发表于 2020年8月9日 02:03:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/63318682.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定