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)