0

I'll start off by saying that I do not know how to code, and I have been using this Python script to convert building models from lat-long into UTM for many years.

The problem is that in all of these years, the coordinates for where I needed to place the 3D building models has been in the northern hemisphere, whereas this current project requires the building models to be placed in the southern hemisphere.

I enter the script as follows into the Windows command line: "villageUTMconv9.py aman_corners.csv 23". (23 being the UTM zone, though it is supposed to be 23S, but when I enter that I get an error) Instead of placing the models in Brazil, they are placed in Greenland.

Should I copy the code here? One is in a folder that has six files, the first of each file in sequence being a "python file" and the second being a "compiled python file". I can only open the "fython file" in a program called Visual Studio Code: init, init / fromUTM, fromUTM / KMLwrite, KMLwrite.

The other one is a python file called "villageUTMconv9", which I assume is the main one because when I open it, it has a lot of code in it, comments, et cetera. I apologize again, I do not know how to code but I can link each of those files here if needed.

EDIT: this is from the "fromUTM" file:

```py

import pyproj

def repj(easting, northing, utz): SProj = pyproj.Proj(proj='utm', datum='WGS84', zone=utz) TProj = pyproj.Proj(proj='latlon', datum='WGS84') lon, lat = pyproj.transform(SProj, TProj, easting, northing) cooords = [lon, lat] return cooords


'''  Reads a csv file with building lat/long coordinates, converts those coordinates into UTM,
converts referenced OpenFlight models into Collada, converts model texture images into .dds format.
Requires the OSG package for the osgconv and nvdxt programs for conversion.
Also requires the  villageConverter directory with its contained python modules.
Requires fields for (and labeled as) EASTING, NORTHING, MODEL, and AOO (angle of orientation).
Assumes that the MODEL field will be empty for all additional point listings, and will only contain
data if that row defines a new building.  Currently requires model names to be 3 or more characters,
which is automatic with the .flt extension anyway.

'''
import csv
import sys
import os
import shutil
#  import time
import villageConverter.fromUTM as fromUTM
import villageConverter.KMLwrite as KMLwrite


def minMaxFive(line):   
    begpos = line.find('>') + 1
    stub = line[begpos:]
    endpos = stub.find('<')
    valstring = stub[:endpos]
    values = valstring.split()
    floatVals = []
    for thisNum in values:
        thisFloat = float(thisNum) * 5
        floatVals.append(thisFloat)
    txtvals = str(floatVals[0]) + ' ' + str(floatVals[1])
    tempLine = line[:begpos] + txtvals + '</MinMax>\n'
    return tempLine

def nocomma(thisvalue):
    comlist = []
    for letter in thisvalue:
        if (not(letter==",")):
            comlist.append(letter)
    thisstring = ""
    for item in comlist:
        thisstring += item      # ultra cool that this works for strings
    return(thisstring)

def colladaClean(daeName):
    extPos = daeName.find('.dae')
    inBase = daeName[:extPos]
    inbak = inBase + '_bak.dae'
    if os.path.isfile(inbak):
        os.remove(inbak)
    shutil.copy (daeName, inbak)
    with open(inbak) as f:
        colladaList = f.readlines()
    f.close()
    os.remove(daeName)  # remove original file, now reading from .bak and replacing original
    idx = 0
    colladaLen = len(colladaList)
    with open(daeName, 'wb') as q:
        flag_lod = False
        while ( idx < colladaLen ):
            line = colladaList[idx]
            lineout = line  # lineout is the one that gets changed and finally written
            if '<extra type="LOD">' in line:
                flag_lod = True
            if flag_lod and '<MinMax>' in line:
                lineout = minMaxFive(line)
            if '</extra>' in line:
                flag_lod = False
            if ("<library_effects>" in line):
                while ("</library_effects>" not in line):
                    if ("<phong>" in line):
                        while ("</phong>" not in line):
                            if ("<diffuse>" in line):
                                q.write(bytes(lineout))  # just write the tag
                                idx+=1
                                line = colladaList[idx]
                                lineout = line
                                if ("<texture" in line):    # kill multi-line textures at known position
                                    if ("/>" not in line):
                                        pos = line.find(">")
                                        leftstub = line[:pos]
                                        addend = leftstub + "/>" + "\n"
                                        lineout = addend
                                    q.write(bytes(lineout))
                                    while ("</diffuse>" not in line):  # should not surround the if <texture block
                                        idx+=1
                                        line = colladaList[idx]
                                        lineout = line
                            if ("<transparent>" in line):
                                q.write(bytes(lineout))
                                idx+=1
                                line = colladaList[idx]
                                lineout = line
                                if ("<texture" in line):
                                    if ("/>" not in line):
                                        pos = line.find(">")
                                        leftstub = line[:pos]
                                        addend = leftstub + "/>" + "\n"
                                        lineout = addend
                                    q.write(bytes(lineout))   # writes <texture ...... />
                                while ("</transparent>" not in line):
                                    idx+=1
                                    line = colladaList[idx]
                                    lineout = line
                            q.write(bytes(lineout))
                            idx+=1
                            line = colladaList[idx]
                            lineout = line
                    if line.count('.dds'):
                        dist1 = line.find('>')
                        dist2 = dist1 + 1
                        stripped = line[dist2:]
                        headstuff = line[:dist2]
                        lineout = headstuff + '../images/' + stripped
                    q.write(bytes(lineout)) # a while loop should have write statement.  an if block should NOT
                    idx+=1
                    line = colladaList[idx]
                    lineout = line      
            q.write(bytes(lineout))
            idx+=1
    q.close()
    os.remove(inbak)


def addpath(imagefile):
    loc = imagefile.find("<init_from>")
    whitespace = imagefile[:loc]
    dataloc = loc + 11  # just past the > of the begin tag
    stub = imagefile[dataloc:]  # /this/that/otherfile.jpg</init_from>
    loc = stub.find("</init_from>")
    imgpath = stub[:loc]    # /this/that/otherfile.jpg
    if ("/" in imgpath):
        slashloc = imgpath.rfind("/")
        basenameloc = slashloc + 1
        basename = imgpath[basenameloc:]
    namewithpath = "./images/" + basename   
    line = whitespace + "<init_from>" + namewithpath + "</init_from>" + "\n"
    return(line)

    
def colladaCustomize(daeName):
    extPos = daeName.find('.dae')
    inBase = daeName[:extPos]
    inbak = inBase + '_bak2.dae'
    if os.path.isfile(inbak):
        os.remove(inbak)
    shutil.copy (daeName, inbak)
    with open(inbak) as g:
        colladaList = g.readlines()
    g.close()
    os.remove(daeName)  # remove original file, now reading from .bak2 and replacing original
    idx = 0
    iflag=0
    colladaLen = len(colladaList)
    with open(daeName, 'wb') as out:
        while ( idx < colladaLen ):
            line = colladaList[idx]
            lineout = line  # lineout is the one that gets changed and finally written
            if ("<library_effects>" in line):
                while ("</library_effects>" not in line):   # eat library_effects
                    if ("<sampler2D>" in line): # confine this operation to within <sampler2D> region 
                        while ("</sampler2D>" not in line):
                            if ("<minfilter>" in line):
                                pos = line.find("<")
                                spaces = line[:pos]
                                lineout = spaces + "<minfilter>LINEAR_MIPMAP_LINEAR</minfilter>" + "\n"
                            if ("<magfilter>" in line):
                                pos = line.find("<")
                                spaces = line[:pos]
                                lineout = spaces + "<magfilter>LINEAR_MIPMAP_LINEAR</magfilter>" + "\n"
                            out.write(bytes(lineout))
                            idx+=1
                            line = colladaList[idx]
                            lineout = line
                    out.write(bytes(lineout))
                    idx+=1
                    line = colladaList[idx]
                    lineout=line
            if ('<image_id="' in line):
                iflag = 1   # set state to image definition
            if (iflag and ("<init_from>" in line)):
                lineout = addpath(line)
            if ("</image>" in line):
                iflag = 0
            out.write(bytes(lineout))
            idx+=1
    out.close()
    os.remove(inbak)
    colladaList = []

    
def makeKMLsubDirs(filebaseName):
    modelDir = filebaseName + '\models'
    os.makedirs(modelDir)  
    imageDir = filebaseName + '\images'
    os.mkdir(imageDir)
    return [modelDir, imageDir]
    
def getLocation(row):
    thisEasting = nocomma(row[column_east])
    thisNorthing = nocomma(row[column_north])
    x, y = fromUTM.repj(thisEasting, thisNorthing, uzone) 
    getLong = str(x)
    getLat = str(y)
    getRotation = row[column_angleOfOrientation]
    getFile = row[column_model]
    return [getLong, getLat, getRotation, getFile]


def countbldgDict(thisModelFile, bldgDict):
    #  updates a dictionary, tracking how many of each model we have
    ext = thisModelFile.find('.flt')
    modelBaseNm = thisModelFile[:ext]  # modelBaseNm gets the base name for the model
    if modelBaseNm in bldgDict:
        bldgDict[modelBaseNm] += 1
    else:
        bldgDict[modelBaseNm] = 1
    if bldgDict[modelBaseNm] > 1:
        buildingName = modelBaseNm + '_' + str(bldgDict[modelBaseNm])  # ie: shed_4
    else:
        buildingName = modelBaseNm
    dirname = buildingName + '\models'
    return [dirname, buildingName]
    


try:
    infile = str(sys.argv[1])
    uzone = int(sys.argv[2])
except IndexError:
    print('Please specify the name of the csv file and then the UTM zone as an integer. Example: S:\> villageUTMconv.py ripley.csv 15')
    exit(0)

# Invoke a csv reader module from the filename
csvData = csv.reader(open(infile),delimiter=',',quotechar='"')

# rowdex = 0  #  used to keep loop just to the first row of the CSV file
    # since the csv reader is not subscriptable, but uses iterator protocol
# for row in csvData:       # maybe change this to  row = csvData.__next__()

row = next(csvData)
for i in range(len(row)):
    if row[i] == 'EASTING':
        column_east = i
        continue
    if row[i] == 'NORTHING':
        column_north = i
        continue
    if row[i] == 'MODEL':
        column_model = i
        continue
    if row[i] == 'AOO':
        column_angleOfOrientation = i
        continue
#rowdex +=1
#if rowdex > 0:
#   break
        
    
# column_east, column_north, column_model, and column_angleOfOrientation  are the column positions for data in the CSV file
# this changes on a case-by-case basis because of how Terra Vista exports data      
    
# bldgDict = {'dinglewaffer': 1}  # set up dictionary used to count same name bldgDicts
# del bldgDict['dinglewaffer']
bldgDict = {}


for row in csvData:
    if len(row[column_model]) > 2:  # if the "model" field is empty, this row is subordinate data - another bldg corner
        thisLongitude, thisLatitude, thisRotation, thisModelFile = getLocation(row)
        if (not os.access(thisModelFile, os.F_OK)):
            print("  Error: ", thisModelFile, "  not found!  Exiting.")
            exit(1)
        dirname, buildingName = countbldgDict(thisModelFile, bldgDict)
        if os.access(dirname, os.F_OK):  # check for pre-existing model before running converter - this should never get triggered
            pass
        else:
            outfile = buildingName + '.dae'  # outfile could be bldgDict_04.dae  or shed.dae.  This is the target model file.
            oscomd = "osgconv --compressed " + thisModelFile + ' ' + outfile   # run converter, leaves dae & dds files in work directory
            os.system(oscomd)
#           time.sleep(10)  # wait for converter  -  would be much better to use Python's subprocess feature & wait for converter to finish
            modelDir, imageDir = makeKMLsubDirs(buildingName)   # create directories /images and /models
            colladaClean(outfile)       # clean up some shader problems with the collada files
            colladaCustomize(outfile)   # customize image paths and set minfilter and magfilter for Rigel
            newpath = modelDir + "\\" + outfile 
            os.rename(outfile, newpath)     # move the Collada file
            for itemname in os.listdir('.'):            # look for dds files and move them
                    if '.dds' in itemname:
                        oscommand = "nvdxt -overwrite -outsamedir -file " + itemname + " -flip" # un-flips the dds images
                        os.system(oscommand)
                        newpath = imageDir + "\\" + itemname
                        os.rename(itemname, newpath)    # move images into the imageDir
#                       print('rename dds ', itemname, 'as', newpath)
            KMLwrite.writer(thisLongitude, thisLatitude, thisRotation, buildingName, outfile)



1
  • This code posted is not where the coordinate conversions are happening. The conversion is happening in another script called villageConverter which is being imported from another Python script. Look in the same directory as the script you have posted and look for something called villageConvert.fromUTM.py. Post that script to your question.
    – GBG
    Sep 22, 2022 at 15:51

2 Answers 2

1

https://proj.org/operations/projections/utm.html#cmdoption-arg-south

You need the south flag for the southern hemisphere.

SProj = pyproj.Proj(proj='utm', datum='WGS84', zone=utz, south=True)
0

A friend of mine found the solution, which is in the "fromUTM" file (the one above the code, which I messed up. He added a line of code and had me change the cooords = [lon, lat] section, making it -lon, -lat. Worked out perfectly.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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