#IMPORTS import sys, os, operator, itertools, random, math #GLOBALS NONEVALUE = None """ SEE ALSO http://code.activestate.com/recipes/189971-basic-linear-algebra-matrix/ http://users.rcn.com/python/download/python.htm """ #FUNCTIONS def Resize(rows, newlength, stretchmethod="not specified", gapvalue="not specified"): "front end used by user, determines if special nested list resizing or single list" if isinstance(rows[0], (list,tuple)): #input list is a sequence of list (but only the first item is checked) #needs to have same nr of list items in all sublists crosssection = itertools.izip(*rows) grad_crosssection = [ _Resize(spectrum,newlength,stretchmethod,gapvalue) for spectrum in crosssection ] gradient = [list(each) for each in itertools.izip(*grad_crosssection)] return gradient else: #just a single list of values return _Resize(rows, newlength, stretchmethod, gapvalue) def Transpose(listoflists): "must get a 2d grid, ie a list of lists, with all sublists having equal lengths" transposed = [list(each) for each in itertools.izip(*listoflists)] return transposed #BUILTINS def _Resize(rows, newlength, stretchmethod="not specified", gapvalue="not specified"): "behind the scenes, does the actual work, only for a single flat list" #return input as is if no difference in length if newlength == len(rows): return rows #set gap if gapvalue == "not specified": gapvalue = NONEVALUE #set auto stretchmode if stretchmethod == "not specified": if isinstance(rows[0], (int,float)): stretchmethod = "interpolate" else: stretchmethod = "duplicate" #reduce newlength newlength -= 1 #assign first value outlist = [rows[0]] relspreadindexgen = (index/float(len(rows)-1) for index in xrange(1,len(rows))) #warning a little hacky by skipping first index cus is assigned auto relspreadindex = next(relspreadindexgen) spreadflag = False for each in xrange(1, newlength): #relative positions rel = each/float(newlength) relindex = (len(rows)-1) * rel basenr,decimals = str(relindex).split(".") relbwindex = float("0."+decimals) #determine equivalent value if stretchmethod=="interpolate": maybecurrelval = rows[int(relindex)] if maybecurrelval != gapvalue: #ALMOST THERE BUT NOT QUITE #DOES SOME WEIRD BACKTRACKING #... currelval = rows[int(relindex)] #make sure next value to interpolate to is valid testindex = int(relindex)+1 while testindex < len(rows)-1 and rows[testindex] == gapvalue: #if not then interpolate until next valid item testindex += 1 nextrelval = rows[testindex] #assign value relbwval = currelval + (nextrelval - currelval) * relbwindex #basenr pluss interindex percent interpolation of diff to next item elif stretchmethod=="duplicate": relbwval = rows[int(round(relindex))] #no interpolation possible, so just copy each time elif stretchmethod=="spread": if rel >= relspreadindex: spreadindex = int(len(rows)*relspreadindex) relbwval = rows[spreadindex] #spread values further apart so as to leave gaps in between relspreadindex = next(relspreadindexgen) else: relbwval = gapvalue #assign each value outlist.append(relbwval) #assign last value outlist.append(rows[-1]) return outlist def _InterpolateValue(value, otherinput, method): "method can be linear, IDW, etc..." pass #CLASSES class _1dData: """ Most basic of all list types. Contains data values but is just a meaningless arbitrary list if not embedded in some other list type. It can be embedded in a 2dsurfacegrid to represent a theme located along horizantal x lines. Or embedded in a 4dtimegrid to represent a theme changing over time, without any spatial properties. Maybe same as Listy below?? """ pass class _Cell: def __init__(self, xpos, ypos, value): self.x = xpos self.y = ypos self.value = value class _2dSurfaceGrid: "horizontal lines up and down along y axis" #BUILTINS def __init__(self, twoDlist=None, emptydims="not specified"): #add some error checking... #... if not twoDlist: if emptydims == "not specified": emptydims = (50,50) width,height = emptydims twoDlist = [[NONEVALUE for _ in xrange(width)] for _ in xrange(height)] self.grid = Listy(*twoDlist) self.height = len(self.grid.lists) self.width = len(self.grid.lists[0]) self.knowncells = self._GetKnownCells() def __iter__(self): for ypos, horizline in enumerate(self.grid.lists): for xpos, xpoint in enumerate(horizline): yield _Cell(xpos, ypos, value=xpoint) def __str__(self): return str(self.grid) #FUNCTIONS def RandomPoints(self, value="random", valuerange="not specified", nrpoints="not specified"): if nrpoints == "not specified": nrpoints = int(self.width*self.height*0.10) #10 percent of all cells if valuerange == "not specified": valuerange = (0,250) randomvalue = False if value == "random": randomvalue = True for _ in xrange(nrpoints): if randomvalue: value = random.randrange(*valuerange) xindex = random.randrange(self.width) yindex = random.randrange(self.height) self.grid.lists[yindex][xindex] = value self.knowncells = self._GetKnownCells() def Interpolate(self, method="IDW", **options): "ie fill any gaps in grid with interpolation" if method == "IDW": self._IDW(options) def ChangeValues(self, expression): pass def SelectQuery(self, query): pass def Show(self): import numpy, PIL, PIL.Image, PIL.ImageTk, PIL.ImageDraw import Tkinter as tk import colour win = tk.Tk() nparr = numpy.array(self.grid.lists) npmin = numpy.min(nparr) npmax = numpy.max(nparr) minmaxdiff = npmax-npmin colorstops = [colour.Color("red").rgb,colour.Color("yellow").rgb,colour.Color("green").rgb] colorstops = [list(each) for each in colorstops] colorgrad = Listy(*colorstops) colorgrad.Convert("250*value") colorgrad.Resize(int(minmaxdiff)) valuerange = range(int(npmin),int(npmax)) colordict = dict(zip(valuerange,colorgrad.lists)) print len(valuerange),len(colorgrad.lists),len(colordict) print "minmax",npmin,npmax for ypos,horizline in enumerate(self.grid.lists): for xpos,value in enumerate(horizline): relval = value/float(npmax) self.grid.lists[ypos][xpos] = colorgrad.lists[int((len(colorgrad.lists)-1)*relval)] nparr = numpy.array(self.grid.lists,"uint8") print "np shape",nparr.shape img = PIL.Image.fromarray(nparr) drawer = PIL.ImageDraw.ImageDraw(img) size = 3 for knowncell in self.knowncells: x,y = (knowncell.x,knowncell.y) drawer.ellipse((x-size,y-size,x+size,y+size),fill="black") img.save("C:/Users/BIGKIMO/Desktop/test.png") tkimg = PIL.ImageTk.PhotoImage(img) lbl = tk.Label(win, image=tkimg) lbl.pack() win.mainloop() #INTERNAL USE ONLY def _GetKnownCells(self): knowncellslist = [] for cell in self: if cell.value != NONEVALUE: knowncellslist.append(cell) return knowncellslist def _IDW(self, options): #retrieve input options neighbours = options.get("neighbours",int(len(self.knowncells)*0.10)) #default neighbours is 10 percent of known points sensitivity = options.get("sensitivity",3) #same as power, ie that high sensitivity means much more effect from far away points #some defs def _calcvalue(unknowncell, knowncells): weighted_values_sum = 0.0 sum_of_weights = 0.0 for knowncell in knowncells: weight = ((unknowncell.x-knowncell.x)**2 + (unknowncell.y-knowncell.y)**2)**(-sensitivity/2.0) sum_of_weights += weight weighted_values_sum += weight * knowncell.value return weighted_values_sum / sum_of_weights #calculate value for unknowncell in self: if unknowncell.value == NONEVALUE: #only calculate for unknown points self.grid.lists[unknowncell.y][unknowncell.x] = _calcvalue(unknowncell, self.knowncells) class _3dSpaceGrid: """z axis. Works by rendering one surface at a time, starting with lowest, that way rendering higher up/closer to the eye points on top of lower/further away points which gives a 3d effect. Just need to find a way to transform each surface to the way it should look like from different angles. Note: Link to a function to create the transform coeffs for PIL's perspective transform: http://stackoverflow.com/questions/14177744/how-does-perspective-transformation-work-in-pil OR use ray tracing... http://scratchapixel.com/lessons/3d-basic-lessons/lesson-1-writing-a-simple-raytracer/source-code/ ALSO see basic 3d equations http://www.math.washington.edu/~king/coursedir/m445w04/notes/vector/equations.html """ pass class _4dTimeGrid: """time axis example: for 3dtime in 4dtimegrid: #loops all 3d spaces at different points in time for 2ddepth in 3dtime: #loops all 2d surfaces at different altitudes, from low to high for 1dheight in 2ddepth: #loops all leftright horizantal lines in a 2dgrid for datavalue in 1dheight: #loops all datavalues for whatever theme in a specific 1dline, in a 2dgrid, at a 3daltitude, at a 4d point in time """ pass class Listy(list): "A list-type class with extended functionality and methods. These extra features should only be bindings to functions alredy defined in the general listy module and can be used by anyone, not just the Listy class." def __init__(self, *sequences): self.lists = list(sequences) self.dtype = "numbers" #"maybe do some automatic dtype detection" def __str__(self): maxchar = 50 printstr = "[\n" for eachlist in self.lists: if len(str(eachlist)) > maxchar: printstr += str(eachlist)[:int(maxchar/2.0)]+"..."+str(eachlist)[-int(maxchar/2.0):]+"\n" else: printstr += str(eachlist)+"\n" printstr += "]\n" return printstr #SHAPE AND ORIENTATION def Resize(self, newlength, listdim=None, stretchmethod="not specified", gapvalue="not specified"): if listdim: #resize only a certain dimension self.lists[listdim] = Resize(self.lists[listdim], newlength, stretchmethod, gapvalue) else: #resize all lists??? experimental... self.lists = Resize(self.lists, newlength, stretchmethod, gapvalue) def Transpose(self): self.lists = Transpose(self.lists) def Reshape(self, newshape): "not sure yet how to do..." pass def Split(self, groups_or_indexes): pass #TYPES def Convert(self, dataformat): "it does actually work...but floating values will never be shortened bc no exact precision" toplist = self.lists def execfunc(code, toplist, value, index): exec(code) def recurloop(toplist): for index, value in enumerate(toplist): if isinstance(value, list): listfound = True recurloop(value) else: conversioncode = "toplist[index] = "+dataformat execfunc(conversioncode, toplist, value, index) #begin recurloop(toplist) #ATTRIBUTES @property def minval(self): pass @property def maxval(self): pass @property def shape(self): return tuple([len(eachlist) for eachlist in self.lists]) @property def structure(self): toplist = self.lists depth = 0 spaces = " " structstring = str(len(toplist))+"\n" def recurloop(toplist, structstring, depth, spaces): for item in toplist: if isinstance(item, list): listfound = True depth += 1 structstring += spaces*depth + str(len(item)) + "\n" toplist, structstring, depth, spaces = recurloop(item, structstring, depth, spaces) depth -= 1 return toplist, structstring, depth, spaces #begin item, structstring, depth, spaces = recurloop(toplist, structstring, depth, spaces) return structstring if __name__ == "__main__": print "" print "print and shape and resize test" testlist = [random.randrange(50) for e in xrange(random.randrange(31))] testlisty = Listy(testlist, testlist) print testlisty print testlisty.shape testlisty.Resize(22, listdim=0) print testlisty.shape print testlisty print "" print "hierarchical nested list structure test" nestedlists = [[["anything" for e in xrange(random.randrange(500))] for e in xrange(random.randrange(5))] for d in xrange(random.randrange(6))] nestedlisty = Listy(*nestedlists) print nestedlisty print nestedlisty.structure print "" print "transpose test" listoflists = [range(100) for _ in xrange(100)] gridlisty = Listy(*listoflists) print gridlisty gridlisty.Transpose() print gridlisty print "" print "fill in blanks test" listholes = [1,2,None,4,None,6] gridlisty = Listy(*listholes) print gridlisty gridlisty.Resize(12, stretchmethod="interpolate") print gridlisty print "" print "spread grid test" listoflists = [range(6) for _ in xrange(6)] gridlisty = Listy(*listoflists) print gridlisty #expand sideways (instead of downwards which is default for the multilist resize func) gridlisty.Transpose() gridlisty.Resize(11, stretchmethod="spread") gridlisty.Transpose() #resize again to also expand downwards gridlisty.Resize(11, stretchmethod="spread") print gridlisty #finally try interpolating in between #THIS PART NOT WORKING PROPERLY, GOING ZIGZAG OVER GAPS ## gridlisty.Resize(22, stretchmethod="interpolate") ## print gridlisty ## gridlisty.Resize(11) ## print gridlisty print "" print "gridpoints interpolate test" listoflists = [[random.randrange(200) for _ in xrange(10)] for _ in xrange(10)] #[range(10) for _ in xrange(10)] templisty = Listy(*listoflists) print templisty #spread to create holes templisty.Transpose() templisty.Resize(80, stretchmethod="spread") templisty.Transpose() templisty.Resize(80, stretchmethod="spread") #make into 2dgrid obj testgrid = _2dSurfaceGrid(templisty.lists) print "spread done"#,testgrid #interpolate testgrid.Interpolate("IDW") print "idw done"#,testgrid.grid.lists #reduce decimals ##testgrid.grid.Convert("str(round(value,2))") ##print testgrid #testgrid.Show() print "" print "randompoints interpolate test" #create empty 2dgrid obj testgrid = _2dSurfaceGrid(emptydims=(600,600)) print "grid made"#,testgrid #put random points testgrid.RandomPoints(nrpoints=20) print "points placed"#,testgrid #interpolate import time t=time.clock() testgrid.Interpolate("IDW", sensitivity=4) print time.clock()-t print "idw done"#,testgrid.grid.lists testgrid.Show()