Создание повернутого геопространственного PDF-файла

Я создаю карты походов / пеших прогулок в формате PDF. В качестве дополнительной функции я добавляю в PDF-файл область просмотра со словарем геопространственных измерений. Это необходимо для соотнесения позиций на странице с фактическими географическими координатами.

Я следую документации Adobe на странице https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/adobe_supplement_iso32000.pdf. В простом сценарии это работает нормально: Acrobat с включенным инструментом геопространственного местоположения показывает правильные координаты положения мыши.

По некоторым причинам, связанным с фактической печатью PDF-файлов, я хочу повернуть карту на странице. Насколько я понимаю документ спецификации PDF 7 (раздел 8.8), все, что мне нужно сделать, это предоставить повернутую главную диагональ в BBox словаря области просмотра моей карты. И я этим занимаюсь. (Помимо поворота изображения)

В повернутой версии карты Acrobat правильно распознает главную диагональ прямоугольника. Однако для двух других углов соотношение между географическими (широта / долгота) и геометрическими (x, y) координатами меняется местами.

Кто-нибудь знает, что не так с моим файлом PDF? Примером вертикального типа является https://mdedoes.home.xs4all.nl/maarssen-25000.pdf, а повернутый - https://mdedoes.home.xs4all.nl/maarssen-25000-r.pdf.

Для полноты картины я включил список окон просмотра страницы. (Извлечено из фактического файла с PDFBox)

 VP[0].Name=n:Maarssen
 VP[0].BBox[0]=f:42.5197
 VP[0].BBox[1]=f:552.7559
 VP[0].BBox[2]=f:325.98428
 VP[0].BBox[3]=f:42.5197
 VP[0].Measure:o.Bounds[0]=f:0.0
 VP[0].Measure:o.Bounds[1]=f:0.0
 VP[0].Measure:o.Bounds[2]=f:0.0
 VP[0].Measure:o.Bounds[3]=f:1.0
 VP[0].Measure:o.Bounds[4]=f:1.0
 VP[0].Measure:o.Bounds[5]=f:1.0
 VP[0].Measure:o.Bounds[6]=f:1.0
 VP[0].Measure:o.Bounds[7]=f:0.0
 VP[0].Measure:o.GCS:o.Type=n:GEOGCS
 VP[0].Measure:o.GCS:o.EPSG=i:4289
 VP[0].Measure:o.GCS:o.WKT=s:GEOGCS["Amersfoort", Etc
 VP[0].Measure:o.DCS:o.Type=n:PROJCS
 VP[0].Measure:o.DCS:o.EPSG=i:28992
 VP[0].Measure:o.DCS:o.WKT=s:PROJCS["Amersfoort / RD New", Etc
 VP[0].Measure:o.GPTS[0]=f:52.151005
 VP[0].Measure:o.GPTS[1]=f:4.9930854
 VP[0].Measure:o.GPTS[2]=f:52.173477
 VP[0].Measure:o.GPTS[3]=f:4.9928865
 VP[0].Measure:o.GPTS[4]=f:52.17368
 VP[0].Measure:o.GPTS[5]=f:5.0586777
 VP[0].Measure:o.GPTS[6]=f:52.151207
 VP[0].Measure:o.GPTS[7]=f:5.058843
 VP[0].Measure:o.LPTS[0]=f:0.0
 VP[0].Measure:o.LPTS[1]=f:0.0
 VP[0].Measure:o.LPTS[2]=f:0.0
 VP[0].Measure:o.LPTS[3]=f:1.0
 VP[0].Measure:o.LPTS[4]=f:1.0
 VP[0].Measure:o.LPTS[5]=f:1.0
 VP[0].Measure:o.LPTS[6]=f:1.0
 VP[0].Measure:o.LPTS[7]=f:0.0
 VP[0].Measure:o.PDU[0]=s:M
 VP[0].Measure:o.PDU[1]=s:SQM
 VP[0].Measure:o.PDU[2]=s:DEG
 VP[0].Measure:o.Subtype=n:GEO
 VP[0].Measure:o.Type=n:Viewport

person Mark de Does    schedule 02.01.2020    source источник


Ответы (2)


Массив LPTS сопоставляет географические точки в GPTS с углами вашего окна просмотра относительно системы координат, определенной BBox.

Используйте это значение для LPTS, и широта / долгота будут обрабатываться правильно: [0,0 0,0 1,0 0,0 1,0 1,0 0,0 1,0]

Верхний левый угол - (0,0), нижний правый - (1,1). Ваши баллы GPTS даются в следующем порядке (вверху, слева), (вверху, справа), (внизу, справа), (внизу, слева), поэтому массив LPTS равен (0,0), (1,0), (1 , 1), (0,1).

person iPDFdev    schedule 27.01.2020

Частичный ответ выше (он работает для примера) помог мне сформулировать общее решение:

Если GPTS расположены по углам прямоугольника карты по часовой стрелке, порядок LPTS зависит от угла поворота. Для поворотов на нечетное число, кратное 90 градусам, LPTS идут против часовой стрелки. Для четных чисел LPTS располагаются по часовой стрелке.

Возможно, проще всего понять фрагмент кода:

// Emit GPTS
private static COSArray gpts( GeospatialPdfViewport mapViewport ) {

    Coordinate[] coordinates= new Coordinate[] {
            mapViewport.getGeographicLowerLeft(),
            mapViewport.getGeographicUpperLeft(),
            mapViewport.getGeographicUpperRight(),
            mapViewport.getGeographicLowerRight(),
    };

    COSArray pts= new COSArray();

    for ( Coordinate c: coordinates ) {
        // Lat(y)-Lon(x) rather than Lon-Lat
        pts.add( new COSFloat( (float)c.y ) );
        pts.add( new COSFloat( (float)c.x ) );
    }

    return pts;
}

/**
 * @param odd True for 90 or 270 degrees; False for 0 or 180 degrees
 * @return an LPTS array. The contents depend on the angle of rotation of the viewport
 */
private static COSArray lpts( boolean odd ) {

    if  ( odd ) {
        return toCOSArray( new Coordinate[] {
                new Coordinate( 0, 0 ),
                new Coordinate( 1, 0 ),
                new Coordinate( 1, 1 ),
                new Coordinate( 0, 1 ),             
        } );
    } else {
        return toCOSArray( new Coordinate[] {
                new Coordinate( 0, 0 ),
                new Coordinate( 0, 1 ),             
                new Coordinate( 1, 1 ),
                new Coordinate( 1, 0 ),             
        } );
    }
}

// Emit Bounds
private static COSArray bounds() {
    return toCOSArray( new Coordinate[] {
            new Coordinate( 0, 0 ),
            new Coordinate( 0, 1 ),
            new Coordinate( 1, 1 ),
            new Coordinate( 1, 0 ) } );
}
person Mark de Does    schedule 08.02.2020