การสร้างแผนที่และบันทึกลงในภาพด้วย GeoTools [ปิด]


9

ฉันต้องการสร้างแผนที่ด้วย GeoTools และบันทึกลงในภาพ (เช่น JPEG) ความต้องการของฉันเรียบง่าย:

  1. สร้างแผนที่ของโลกด้วย 2 เลเยอร์: ขอบเขตทางการเมืองและกริด เลเยอร์มาจากแหล่งที่แตกต่างกันและการคาดการณ์ที่แตกต่างกัน
  2. ส่งออกแผนที่ไปยังการคาดการณ์ที่แตกต่างกัน (เช่น "EPSG: 5070", "EPSG: 4326", "EPSG: 54012", "EPSG: 54009" ฯลฯ )
  3. คลิปเอาต์พุตไปยัง AOIs อื่น (เช่น -124.79 ถึง -66.9 lon, 24.4 ถึง 49.4 lat)

ฉันต้องการทำสิ่งนี้โดยทางโปรแกรมผ่าน API จนถึงตอนนี้ฉันประสบความสำเร็จอย่าง จำกัด ฉันเรียนรู้ที่จะสร้างแผนที่และเอาท์พุทในการฉายภาพต่าง ๆ โดยใช้วิธีนี้:

//Step 1: Create map
MapContent map = new MapContent();
map.setTitle("World");

//Step 2: Set projection
CoordinateReferenceSystem crs = CRS.decode("EPSG:5070"); //Conic projection over US
MapViewport vp = map.getViewport();
vp.setCoordinateReferenceSystem(crs);

//Step 3: Add layers to map
CoordinateReferenceSystem mapCRS = map.getCoordinateReferenceSystem();
map.addLayer(reproject(getPoliticalBoundaries(), mapCRS));
map.addLayer(reproject(getGraticules(), mapCRS));

//Step 4: Save image
saveImage(map, "/temp/graticules.jpg", 800);

วิธีการบันทึกตรงจากเว็บไซต์ GeoTools :

public void saveImage(final MapContent map, final String file, final int imageWidth) {

    GTRenderer renderer = new StreamingRenderer();
    renderer.setMapContent(map);

    Rectangle imageBounds = null;
    ReferencedEnvelope mapBounds = null;
    try {
        mapBounds = map.getMaxBounds();
        double heightToWidth = mapBounds.getSpan(1) / mapBounds.getSpan(0);
        imageBounds = new Rectangle(
                0, 0, imageWidth, (int) Math.round(imageWidth * heightToWidth));

    } catch (Exception e) {
        // failed to access map layers
        throw new RuntimeException(e);
    }

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

    Graphics2D gr = image.createGraphics();
    gr.setPaint(Color.WHITE);
    gr.fill(imageBounds);

    try {
        renderer.paint(gr, imageBounds, mapBounds);
        File fileToSave = new File(file);
        ImageIO.write(image, "jpeg", fileToSave);

    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

วิธีการปฏิเสธคือสิ่งประดิษฐ์ของฉัน มันเป็นบิตของการแฮ็ค แต่วิธีเดียวที่ฉันสามารถค้นหาเพื่อส่งภาพไปยังการฉายภาพที่เฉพาะเจาะจง

private static Layer reproject(Layer layer, CoordinateReferenceSystem mapCRS) throws Exception {

    SimpleFeatureSource featureSource = (SimpleFeatureSource) layer.getFeatureSource();


  //Define coordinate transformation
    CoordinateReferenceSystem dataCRS = featureSource.getSchema().getCoordinateReferenceSystem();
    boolean lenient = true; // allow for some error due to different datums
    MathTransform transform = CRS.findMathTransform(dataCRS, mapCRS, lenient);


  //Create new feature collection
    SimpleFeatureCollection copy = FeatureCollections.newCollection("internal");
    SimpleFeatureType featureType = SimpleFeatureTypeBuilder.retype(featureSource.getSchema(), mapCRS);
    SimpleFeatureIterator iterator = featureSource.getFeatures().features();
    try {

        while (iterator.hasNext()) {

            SimpleFeature feature = iterator.next();
            Geometry geometry = (Geometry) feature.getDefaultGeometry();
            Geometry geometry2 = JTS.transform(geometry, transform);
            copy.add( SimpleFeatureBuilder.build( featureType, new Object[]{ geometry2 }, null) );
        }

    }
    catch (Exception e) {
        e.printStackTrace();
    }
    finally {
        iterator.close();
    }


  //Return new layer
    Style style = SLD.createLineStyle(Color.BLACK, 1);
    layer = new FeatureLayer(copy, style);
    layer.setTitle("Graticules");
    return layer;
}

ผลลัพธ์ไม่ดีจริง ๆ :

ผลผลิตจากการคัดแยก

ดังนั้นฉันคิดว่าฉันมีคำถามสองสามข้อที่แตกต่างกัน:

  1. นี่คือการอนุมัติที่ถูกต้องหรือไม่ ฉันจำเป็นต้องปฏิเสธเลเยอร์ใหม่ด้วยตนเองหรือ MapViewport ควรจะทำสิ่งนี้ให้ฉันหรือไม่
  2. ฉันจะคลิปผลลัพธ์ไปยัง AOI ที่เฉพาะเจาะจงได้อย่างไร ฉันได้ลองตั้งค่าขอบเขตโดยใช้วิธี MapViewport.setBounds (ซองจดหมาย) แต่วิธี saveImage ดูเหมือนจะไม่สนใจขอบเขต
  3. ฉันจะให้เส้นละติจูดของฉันแสดงเป็นส่วนโค้งได้อย่างไร มีการตั้งค่าการแปลงที่ฉันหายไปหรือไม่?

ฉันใช้ GeoTools 8.7

คำตอบ:


1

1) แผนที่ควรจัดการการคัดค้านสำหรับคุณ ดูQuickStartสำหรับตัวอย่าง

2) คุณถามแผนที่ว่าเป็นขอบเขตสูงสุดไม่ใช่ขอบเขตปัจจุบันและคุณอาจต้องการคลิปโดย DomainOfValidity ของ CRS เพื่อหลีกเลี่ยงความแปลกประหลาด

3) ฉันไม่แน่ใจว่าคุณกำลังสร้าง graticles ของคุณได้อย่างไร แต่ถ้าคุณใช้โมดูลกริดคุณสามารถเพิ่มความหนาแน่นของบรรทัดเพื่อทำให้กลายเป็นส่วนโค้งได้

แก้ไข ถ้าฉันใช้ states.shp (จาก GeoServer) ฉันจะได้รับสิ่งนี้:

ป้อนคำอธิบายรูปภาพที่นี่

โดยใช้รหัสที่นี่

สิ้นสุดการแก้ไข

ในที่สุดการจัดการฉายภาพได้รับการปรับปรุงเมื่อเร็ว ๆ นี้ดังนั้นคุณอาจต้องการย้ายไปที่ GeoTools 12 หรือ 13

แผนที่ตัวอย่าง


2

คำตอบของ Ian นั้นถูกต้องและฉันได้ทำเครื่องหมายไว้เช่นนั้น เพื่อความสมบูรณ์ของคนอื่นที่อาจสนใจ ...


คำถามที่ 1

ไม่คุณไม่ต้องปฏิเสธชั้นด้วยตนเอง การระบุเส้นโครงบนวิวพอร์ตควรเพียงพอ ตัวอย่าง:

    MapViewport vp = map.getViewport();
    CoordinateReferenceSystem crs = CRS.decode("EPSG:5070");
    vp.setCoordinateReferenceSystem(crs);

คำถามที่ 2

ในการคลิปแผนที่คุณต้องตั้งขอบเขตวิวพอร์ตและอัปเดตฟังก์ชั่น saveImage นี่คือตัวอย่างของการตั้งค่าขอบเขตให้กับขอบเขตการฉายภาพ:

    Extent crsExtent = crs.getDomainOfValidity();
    for (GeographicExtent element : crsExtent.getGeographicElements()) {
        if (element instanceof GeographicBoundingBox) {
            GeographicBoundingBox bounds = (GeographicBoundingBox) element;
            ReferencedEnvelope bbox = new ReferencedEnvelope(
                bounds.getSouthBoundLatitude(),
                bounds.getNorthBoundLatitude(),
                bounds.getWestBoundLongitude(),
                bounds.getEastBoundLongitude(),

                CRS.decode("EPSG:4326")
            );
            ReferencedEnvelope envelope = bbox.transform(crs, true);
            vp.setBounds(envelope);
        }
    }

นอกเหนือจากการตั้งค่าขอบเขตวิวพอร์ตแล้วฟังก์ชันsaveImageควรได้รับการแก้ไขเพื่อใช้ขอบเขตวิวพอร์ตแทน map.getMaxBounds ()

เปลี่ยนแปลง:

mapBounds = map.getMaxBounds();

สำหรับสิ่งนี้:

mapBounds = map.getViewport().getBounds();

นี่คือผลลัพธ์:

เรา


คำถามที่ 3

ขอบคุณคำแนะนำของเอียนฉันสามารถทำให้เส้นละติจูดเป็นเส้นโค้งโดยทำให้สตริงเส้นนั้นหนาแน่นขึ้น นี่คือ snippit สำคัญจากเมธอด getGraticules () ที่อ้างอิงในโพสต์ต้นฉบับ:

  //Add lines of latitude
    for (int y=-90; y<=90; y+=15){
        java.util.ArrayList<Coordinate> coords = new java.util.ArrayList<Coordinate>();
        for (double x=-135; x<=-45; x+=0.5){
            coords.add(new Coordinate(y,x,0));
        }
        LineString line = new LineString(coords.toArray(new Coordinate[coords.size()]), precisionModel, 4326);
        collection.add( SimpleFeatureBuilder.build( TYPE, new Object[]{ line }, null) );
    }

ผลลัพธ์จะเป็นดังนี้:

ผลลัพธ์จากการคัดลอก 2

แม้ว่าวิธีนี้ใช้งานได้ แต่ฉันหวังว่าจะมีการเปลี่ยนแปลงหรือสิ่งที่จะทำให้เส้นโค้งของฉันโค้ง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.