3

I'm trying to convert a .jgw (esri worldfile) into xy corner coordinates for a series of JPEG images im outputting using arcpy.mapping.ExportToJPEG(mxd, "E:\\TEMP\\" + str(x) + ".jpg", df, df_export_width=2900, df_export_height=2200, world_file=True)

0.730507756372400
0.000000000000000
0.000000000000000
-0.682083946764469
1819654.840921224100000
5534353.061382980100000

Ideally I will end up with a tab file looking like this:

!table
!version 300
!charset WindowsLatin1

Definition Table
File "aerial.jpg"
Type "RASTER"
  (2661750.000000,6512125.000000)(0,0) Label "P1",
  (2662000.000000,6512125.000000)(800,0) Label "P2",
  (2662000.000000,6511750.000000)(800,1200)Label "P3",
  (2661750.000000,6511750.000000)(0,1200) Label "P4"
  CoordSys Earth Projection 18, 31, "m", 173, -41, 2510000, 6023150
  Units "m"

Could anyone help me with the conversion math from world to corner, either in principle or even better, in python.

  • This example from esri site may help a bit forums.esri.com/Thread.asp?c=93&f=1149&t=244802 – user30184 Nov 2 '14 at 22:12
  • It might help to add the arcpy tag. You will need to get your rows and columns from your raster to calculate properly... if I help get the numbers can you do the tedious formatting? – Michael Stimson Nov 3 '14 at 0:25
  • yep, no problem doing the tedious stuff, just appreciate the help – AnythingMapping Nov 3 '14 at 0:33
4

The world file is as follows:

X-cell size
rotation (usually 0)
rotation (usually 0)
Y-cell size (always negative)
Upper left X
Upper left Y

Read about it here and from Esri 9.2

Assuming your width/height is in cells and not in map units the other corner is:

#YCellSize is always negative so adding is a subtract which is a double negative
#Thank you to Stefan for pointing out that the world X and Y are centre of pixel
#to get the true extent box you must adjust by half a pixel
XMin = WorldX - (XCellSize / 2)
YMax = WorldY - (YCellSize / 2) 
XMax = (WorldX + (Cols * XCellSize)) - (XCellSize / 2)
YMin = (WorldY + (Rows * YCellSize)) - (YCellSize / 2)

If you don't know the number of rows and columns (XCells = cols, YCells = rows) you can get them using a describe statement (raster band properties):

descObj = arcpy.Describe(r"c:\path\to\raster.ext")
XCells = descObj.width
YCells = descObj.height

The properties of the describe object depend on what is being described; it will have multiple property types. Then open the world file and read the values:

with open(r"c:\path\to\raster.etw",'r') as WorldFile:
    XCellSize = float(WorldFile.readline())
    dont_care = WorldFile.readline() # should be 0
    dont_care = WorldFile.readline() # should be 0
    YCellSize = float(WorldFile.readline())
    WorldX = float(WorldFile.readline())
    WorldY = float(WorldFile.readline())

The same values can be obtained from the describe object but I'd trust them more direct from the world file; the extent can contain a bit of a buffer around the image for display purposes.

  • this is brilliant thanks, ill let you know how I go – AnythingMapping Nov 3 '14 at 1:34
  • One minor change, I had used WorldFile.read() where it should be WorldFile.readline(), see edits. – Michael Stimson Nov 3 '14 at 1:40
  • 3
    Note that the upper left X and upper left Y in the worldfile is in the center of the pixel, so if your're looking for the bounding rectangle you need to add or subtract one half cell size. – Stefan Nov 3 '14 at 8:08
  • Absolutely correct @Stefan. That is very important to know; depending on if you need your box to line up with the centres of the cells or the edges of the cells - note, if you need the box to pass through the cell centres remove one row and column or you will overshoot by half a cell on the other side. – Michael Stimson Nov 3 '14 at 23:25
  • once I've got the top left and right corners how do I get the bottom corners? – AnythingMapping Nov 4 '14 at 4:09
1

You can use an affine package to do all of this.

import affine

# Read a six line world file
# with open('aerial.jpw', 'r') as fp:
#   a = affine.loadsw(fp.read())

# or load raw text here
a = affine.loadsw('''\
0.730507756372400
0.000000000000000
0.000000000000000
-0.682083946764469
1819654.840921224100000
5534353.061382980100000
''')
print(a)
# | 0.73, 0.00, 1819654.48|
# | 0.00,-0.68, 5534353.40|
# | 0.00, 0.00, 1.00|

Note that loadsw transforms the Esri world file from the pixel-centre convention (0.5, 0.5) to a more general pixel-corner convention, where (0, 0) is the upper left corner of the first pixel, and (1, 1) is the lower right corner of the same first pixel.

To convert from pixel space to coordinate space, multiply the affine transformation object with a coordinate tuple. For example, here are the four corners of a raster:

xsize = 2900
ysize = 2200
# Process each combination of raster pixel space
for (col, row) in [(0, 0), (xsize, 0), (xsize, ysize), (0, ysize)]:
    x, y = a * (col, row)
    print('(%.6f, %.6f) (%d, %d)' % (x, y, col, row))
# (1819654.475667, 5534353.402425) (0, 0)
# (1821772.948161, 5534353.402425) (2900, 0)
# (1821772.948161, 5532852.817742) (2900, 2200)
# (1819654.475667, 5532852.817742) (0, 2200)
1

What Arcgis (and QGIS for example) is generating is a worldfile (see details here). And what you need to produce is a TAB file (a equivalent georeference file).

As you didn't mention which way it has to be done, and should you have Mapinfo users to adress, you should know Mapinfo is able to generate this file when you provide a raster file with its corresponding worldfile (.wld, .tfw for tif, .jgw for jpg, etc). Mapinfo asks only for the right projection, as the worldfile doesn't contain this information (in the TAB file, it's in the line : CoordSys Earth Projection 18, 31, "m", 173, -41, 2510000, 6023150).

To automate this, you might be interested to know a french developer has created a great mbx tool for Mapinfo that is supposed to do exactly what you want to do. It's named UT4RT : Universal Translator for Raster Tab and is able to take a bunch a raster files + world-file and automatically generate the TAB equivalent (and it does more). I had a try, it seems to work properly on the latest MI version. If you need translation from french , please comment here, i'll provide you with some additional info. I could get in touch with the developer should you need the source files for translation.

UT4RT

0

Get bounding box from .jgw world file

Sample .jgw file

0.000196149135716825
0.0
0.0
-0.000129561535707478
38.8670079999999913
48.8147118793772563

Function to convert world file info into BoundingBox

function worldFileToBoundingBox( pixelSizeX,pixelSizeY,rotationX,rotaionY,centerCoordX,centerCoordY,imagewidth,imageHeight)
{ 
  var minX = centerCoordX - (pixelSizeX / 2);//move from center of pixel box to edge of box
  var maxY = centerCoordY - (pixelSizeY / 2);//move from center of pixel box to edge of box

  var maxX = minX + (pixelSizeX * imagewidth);
  var minY = maxY + (pixelSizeY * imageHeight);


  /*var minX=x1<x2?x1:x2;
  var maxX=x1<x2?x2:x1;

  var minY=y1<y2?y1:y2;
  var maxY=y1<y2?y2:y1;*/ 


  var data = {
    minX: minX,
    minY: minY,
    maxX: maxX,
    maxY: maxY
  };
//console.log(data);
return data;

}

initialize variables

var pixelSizeX=0.000196149135716825;
var pixelSizeY=-0.000129561535707478;
var rotationX=0;
var rotaionY=0;
var centerCoordX=38.8670079999999913;
var centerCoordY=48.8147118793772563;
var imagewidth=866;
var imageHeight=5656;

call function

worldFileToBoundingBox( pixelSizeX,pixelSizeY,rotationX,rotaionY,centerCoordX,centerCoordY,imagewidth,imageHeight)

Sample output

{minX: 38.86690992543213, minY: 48.08197661418361, maxX: 39.036775076962904, maxY: 48.81477666014511}

Fiddle link
Working example in JSFiddle

cheers
happy coding :-)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.