#!/usr/bin/python # -*- coding: utf-8 -*- # Script to create a slightly more useful attempt # at a prospecting guide. areas = ['', '1a','1b','1c','1d', '2a', '2b', '2c', '2d', '3', '4', '5', '6','7', '8a', '8b', '8c', '8d', '9', '10', '11'] areanames = { '': 'Location unclear', '1a': '1a – Plateau: around Top Camp', '1b': '1b – Western plateau near 182', '1c': '1c – Eastern plateau near 204 walk-in path', '1d': '1d – Further plateau around 76', '2a': '2a – Southern Schwarzmooskogel near 201 path and the Nipple', '2b': '2b – Eishöhle area', '2c': '2c – Kaninchenhöhle area', '2d': '2d – Steinbrückenhöhle area', '3': '3 – Bräuning Alm', '4': '4 – Kratzer valley', '5': '5 – Schwarzmoos-Wildensee', '6': '6 – Far plateau', '7': '7 – Egglgrube', '8a': '8a – Loser south face', '8b': '8b – Loser below Dimmelwand', '8c': '8c – Augst See', '8d': '8d – Loser-Hochganger ridge', '9': '9 – Gschwandt Alm', '10': '10 – Altaussee', '11': '11 – Augstbach' } areacolours = { '1a' : '#00ffff', '1b' : '#ff00ff', '1c' : '#ffff00', '1d' : '#ffffff', '2a' : '#ff0000', '2b' : '#00ff00', '2c' : '#008800', '2d' : '#ff9900', '3' : '#880000', '4' : '#0000ff', '6' : '#000000', # doubles for surface fixed pts, and anything else '7' : '#808080' } cavelists={} for a in areas: cavelists[a]=[] #check to see if cmd is on $PATH def exists_in_path(cmd): # can't search the path if a directory is specified assert not os.path.dirname(cmd) extensions = os.environ.get("PATHEXT", "").split(os.pathsep) for directory in os.environ.get("PATH", "").split(os.pathsep): base = os.path.join(directory, cmd) options = [base] + [(base + ext) for ext in extensions] for filename in options: if os.path.exists(filename): return True return False def chomp(s): if not s: return s if(s[-1]=="\n"): return s[:-1] else: return s def find_effective_number(c): """Determine an appropriate number to use.""" if c["Kataster Number"]: return c["Kataster Number"] else: return c["Unofficial number"] def longnumber(c, number): """Both numbers""" if (c["Unofficial number"] and c["Unofficial number"] != number): return number + " (" + c["Unofficial number"] + ")" else: return number def find_location(cave): for fixtype in ["tag point in dataset", "other point in dataset", "exact entrance in dataset (drip line/highest enclosed contour)", "GPS post SA", "GPS pre SA"]: if(cave[fixtype]): return positions[cave[fixtype]] return 0 def is_explored(cave): s = cave["Kat Status Code"] if(not s): return "" s = s.replace("(?)","") if s[-1] == " ": s=s[:-1] if (not s): print "Rogue space in " + find_effective_number(cave) return "" code = s[-1] if code == '+': status = "good" elif code in ['=', 'x']: status = "bad" elif code in ['-', '?']: status = "awful" else: return "%s" % code return "%s" % (status, code) def is_tagged(cave): s = chomp(cave["Marking"]) s = re.sub(r'\s+(?:\(\?\))?$', "", s) if not s and cave["Multiple entrances"] in ["yes", "entrance", "last entrance"]: return "" if s == "Tag": return "%s" % s if s in ["Retag", "Paint", "Spit"]: return "%s" % s if s == "Unmarked": return "None" if s == "": return "" print "Unrecognised marking status on %s: %s" % (find_effective_number(cave), repr(s)) return "ERROR" def findability_color(cave): if cave["Findability"] == "Surveyed": return "good" elif cave["Findability"] == "Refindable": return "bad" elif cave["Findability"] == "Lost": return "awful" else: return "" def is_underground_surveyed(cave): s = chomp(cave["Underground drawn survey"]) if(cave["Multiple entrances"] not in ["", "yes"]): return "" if not s: return "None" if s and (s.find(" -1 or s.find(" -1): return "Yes" else: return "Missing" def have_survey_data(cave): if(cave["Multiple entrances"] not in ["", "yes"]): return "" s = chomp(cave["Underground centre line"]) if s: return "Yes" s = chomp(cave["Survex file to get length and depth"]) if s: return "Yes" if cave["Kat Status Code"] and cave["Kat Status Code"][0] == '1': # Cave < 50m deep and < 50m long... # Sadly this band includes caves we really ought to have data for as # well as caves small enough that a grade 1 sketch is justifiable. return "<50m" s = chomp(cave["Underground drawn survey"]) if s: return "Missing" return "None" def has_photo(cave): s = chomp(cave["Photo of location"]) if ((cave["Multiple entrances"] not in ["", "yes"]) and chomp(cave["Autogen file"]) == ""): return "" if not s: return "None" if s and (s.find(" -1 or s.find(" -1): return "Yes" else: return "Missing" def find_label(cave, number): t = longnumber(cave, number) + " " if(cave["Name"] and cave["Unofficial Name"]): t += cave["Name"] + " (" + cave["Unofficial Name"] + ")" elif cave["Name"]: t += cave["Name"] else: t += cave["Unofficial Name"] try: n = number if(n[-1] in string.lowercase): n = n[:-1] except: n = "NONE" if lengths.has_key(n): t += (" %sm long %sm deep" % (lengths[n][0], lengths[n][1])) return t def get_img_name(maparea): filename = "prospecting_guide" if maparea != "all": filename += "_" + maparea + "area" if showbg: filename += ".jpg" else: filename += ".png" return filename # Parameters for big map and zoomed subarea maps: # big map first (zoom factor ignored) maps = { # id left top right bottom zoom # G&K G&K G&K G&K factor "all": [34950.9, 86300.0, 38325.0, 80895.6, 1.0, "All"], "40": [36275.6, 82392.5, 36780.3, 81800.0, 3.0, "Eishöhle"], "76": [35440.0, 83220.0, 36090.0, 82670.0, 3.0, "Eislufthöhle"], "204": [36354.1, 84154.5, 37047.4, 83399.7, 3.0, "Steinbrückenhöhle"], "tc": [35230.0, 82690.0, 36110.0, 82100.0, 3.0, "Near Top Camp"], "grieß": [36000.0, 86300.0, 38320.0, 84400.0, 4.0, "Grießkogel Area"], } # Keys in the order in which we want the maps output mapcodes = ["all", "grieß","40", "76", "204", "tc"] # Field codes L = 0 T = 1 R = 2 B = 3 ZOOM = 4 DESC = 5 import Image, ImageDraw, ImageFont, string, os, sys for FONT in [ "/usr/share/fonts/truetype/freefont/FreeSans.ttf", "/usr/X11R6/lib/X11/fonts/truetype/arial.ttf", "C:\WINNT\Fonts\ARIAL.TTF" ]: if os.path.isfile(FONT): break SIZE = 8 myFont = ImageFont.truetype(FONT, SIZE) showbg = not(len(sys.argv) > 1 and sys.argv[1] == '--white') mainImage = Image.open("pguidemap.jpg") if not showbg: mainImage = Image.new("RGB", mainImage.size, '#ffffff') imgs = {} draws = {} imgmaps = {} factors = {} for maparea in maps.keys(): m = maps[maparea] if maparea == "all": img = mainImage else: M = maps['all'] W, H = mainImage.size l = int((m[L] - M[L]) / (M[R] - M[L]) * W) t = int((m[T] - M[T]) / (M[B] - M[T]) * H) r = int((m[R] - M[L]) / (M[R] - M[L]) * W) b = int((m[B] - M[T]) / (M[B] - M[T]) * H) img = mainImage.crop((l, t, r, b)) w = int(round(m[ZOOM] * (m[R] - m[L]) / (M[R] - M[L]) * W)) h = int(round(m[ZOOM] * (m[B] - m[T]) / (M[B] - M[T]) * H)) print "%s: H %d W %d" % (maparea, h, w) img = img.resize((w, h), Image.BICUBIC) imgs[maparea] = img draw = ImageDraw.Draw(img) draw.setfont(myFont) draws[maparea] = draw imgmaps[maparea] = [] # Draw scale bar m100 = int(100 / (m[R] - m[L]) * img.size[0]) draw.line([10, SIZE*3, 10, SIZE*2], fill='#000000') draw.line([10, SIZE*2, 10+m100, SIZE*2], fill='#000000') draw.line([10+m100, SIZE * 3, 10+m100, SIZE*2], fill='#000000') label = "100m" draw.text([10 + (m100 - draw.textsize(label)[0]) / 2, SIZE/2], label, fill='#000000') factors[maparea] = img.size[0] / (m[R] - m[L]), img.size[1] / (m[T] - m[B]) def mungecoord(x, y, mapcode): # Top of Zinken is 73 1201 = dataset 34542 81967 # Top of Hinter is 1073 562 = dataset 36670 83317 # image is 1417 by 2201 # FACTOR1 = 1000.0 / (36670.0-34542.0) # FACTOR2 = (1201.0-562.0) / (83317 - 81967) # FACTOR = (FACTOR1 + FACTOR2)/2 # The factors aren't the same as the scanned map's at a slight angle. I # can't be bothered to fix this. Since we zero on the Hinter it makes # very little difference for caves in the areas round 76 or 204. # xoffset = (x - 36670)*FACTOR # yoffset = (y - 83317)*FACTOR # return (1073 + xoffset, 562 - yoffset) m = maps[mapcode] return ((x - m[L]) * factors[mapcode][0], (m[T] - y) * factors[mapcode][1]) def plot(loctuple, number, area, label): shortnumber = number.replace("—","") if area in areacolours: fillcol = areacolours[area] else: fillcol = 6 for maparea in maps.keys(): (x,y) = map(int, mungecoord(loctuple[0], loctuple[1], maparea)) draw = draws[maparea] imgmaps[maparea].append( [x-4, y-SIZE/2, x+4+draw.textsize(shortnumber)[0], y+SIZE/2, shortnumber, label] ) draw.rectangle([(x+4, y-SIZE/2), (x+4+draw.textsize(shortnumber)[0], y+SIZE/2)], fill="#ffffff") draw.text((x+6,y-SIZE/2), shortnumber, fill="#000000") draw.ellipse([(x-4,y-4),(x+4,y+4)], fill=fillcol, outline="#000000") def writeout_imagemap(data, mapname): # Munge the list of coordinates into a proper image map. # There is a wrinkle here: the HTML spec gives priority # to the _first_ defined elt, so we swap the order! n = len(data) htmlfile.write("" % (mapname, mapname)) for j in xrange(n): try: i = data[n-j-1] htmlfile.write("\n" % (i[0],i[1],i[2],i[3],i[4],i[5])) except: print "Died on", repr( data[n-j-1] ) #raise htmlfile.write("") import csv, re, time cavetabfile = file("../CAVETAB2.CSV") fieldnames=chomp(cavetabfile.next()).replace('"','').split(",") cavetab = csv.DictReader(cavetabfile, fieldnames) print "Munging lengths.dat" lengthsfile = file("../lengths.dat") lengths = {} for l in lengthsfile: t = l.replace("\"","").replace("\n", "").split("\t") lengths[t[0]] = (t[1], t[2]) print "Done" # Draw cave passage print "Munging dump3d output" if exists_in_path("dump3d"): dump3d_binary = "dump3d" else: # assume it's in this directory dump3d_binary = os.path.join(os.path.dirname(os.path.realpath(__file__)),"./dump3d") positions = {} surfacecolour = "#808080" repath = re.compile(r'^(?:182to(?:tc|184)|tctocol|82to97|vd1to161d|161ftod|161etof|161etog|40entlink2|surfnr161|surf161|kansurf)$') for fnm in ("../all.3d", "../alltracks.3d"): for mapcode in draws.keys(): file3d = os.popen(dump3d_binary + " " + fnm) draw = draws[mapcode] lastx, lasty = 0, 0 for l in file3d: match = re.match(r''' ^ #start (MOVE|LINE|NODE) #one of three command types \s+(-?[0-9.]+) #X-co-ord \s+(-?[0-9.]+) #Y-co-ord \s+(-?[0-9.]+) #altitude (?: #grouping without saving result - for station name \s+ #at least one space \[ #opening [ (\S*) #the station name we want to save \] #closing ] )? #end of optional station name group (?: #grouping without saving result - for flags \s+ #at least one space separating from [station] (.*) #all the flags, including spaces )? #end of optional flags group $ #end of the line ''', l, re.VERBOSE) if not match: print "Unknown: " + l continue act,E,N,alt,name,flags = match.groups() # print "act:%s, E:%s, N:%s, name:%s flags:%s" % (act,E,N,name,flags) # Only need to process NODEs once if act == "NODE" and mapcode != "all": continue if not flags: flags = "" E,N = map(float, (E,N)) if act == "NODE": positions[name] = (E,N,float(alt)) continue x,y = map(int, mungecoord(E, N, mapcode)) if act == "LINE": if re.search(r'\bSURFACE\b', flags): if re.search(repath, name): draw.line([lastx, lasty, x, y], fill='#008000') else: draw.line([lastx, lasty, x, y], fill='#808080') else: draw.line([lastx, lasty, x, y], fill="#800080") lastx,lasty = x,y if not file3d.close(): # FIXME: If dump3d can't gives an error and exits with non-zero status, this # doesn't get triggered... print "Running command: %s %s failed: %s" % (dump3d_binary, fnm, file3d) surfacecolour = "#008000" repath = re.compile(r'^') print "Done" cavestoplot = [] for cave in cavetab: if cave["Link file"]: continue number = find_effective_number(cave) if(cave["Multiple entrances"] not in ["", "yes"]): number = "—" + cachednumber + cave["Entrances"] shortnumber = cachednumber if not cave["Area"]: cave["Area"] = cachedarea if not cave["Name"]: cave["Name"] = cachedname else: cachednumber = number shortnumber = number cachedname = cave["Name"] or cave["Unofficial Name"] cachedarea = cave["Area"] area = cave["Area"] # We have some areas like '2b or 4 (unclear)' - just chop the space # and everything after it for these. area = re.sub(r' .*', "", area) if area == '1626' or area == 'nonexistent': continue try: loctuple = find_location(cave) except: print "Unable to find location for %s" % (number) else: label = find_label(cave, shortnumber) if(cave["Multiple entrances"] == "yes"): locn = " " elif(loctuple): locn = "%d%d%d" % loctuple cavestoplot.append((loctuple, number, area, label)) else: locn = "" % findability_color(cave) + (cave["Findability"] or '?') + "" try: cavelists[area].append((number, cave, locn)) except: print "Bad area '%s' for cave %s" % (area, number) if showbg: htmlfile = file("/dev/null", "w") else: htmlfile = file("../../handbook/prospecting_guide.html", "w") htmlfile.write("Prospecting Guide\n") htmlfile.write("\n"); htmlfile.write(""" """) htmlfile.write("

Prospecting Guide

") htmlfile.write("

Generated " + time.strftime("%Y-%m-%d %H:%M:%S %Z") + " by " + sys.argv[0] + "

\n") htmlfile.write("

Notes:

  • A marking status of \"Retag\" means a tag is in place but it carries a provisional number, or in some cases an incorrect number, and needs replacing with a new tag.
  • \n
  • Kataster status codes indicate the size of a cave, its character and its exploration status, as described here.
  • For more info on each cave, see the links to detailed description pages.
\n") for maparea in mapcodes: filename = get_img_name(maparea) if maparea != "all": htmlfile.write("

%s area detail

\n" % (maparea, maps[maparea][DESC])) basename = filename[0:-3] htmlfile.write("\n" % (maparea, maparea, basename, maparea, basename, maparea, maparea)) htmlfile.write("Note: this requires a login to work!\n") htmlfile.write("

\n" % imgs[maparea].size) cachednumber = "" cachedarea = "" cachedname = "" # Plot the subareas on the full map for maparea in maps.keys(): if maparea == "all": continue m = maps[maparea] l,t = mungecoord(m[L], m[T], "all") r,b = mungecoord(m[R], m[B], "all") text = maparea + " map" textlen = draws['all'].textsize(text)[0] + 3 draws['all'].rectangle([l, t, l+textlen, t+SIZE+2], fill='#ffffff') draws['all'].text((l+2, t+1), text, fill="#000000") imgmaps['all'].append( [l, t, l+textlen, t+SIZE+2, "submap" + maparea, maparea + " subarea map"] ) draws['all'].rectangle([l, t, r, b], outline='#777777') draws['all'].rectangle([l, t, l+textlen, t+SIZE+2], outline='#777777') # Plot faked positions first so that real caves go on top of the large circles fakedpositions = file("fakedpositions.dat") for p in fakedpositions: try: (x,y,d,name) = re.match(r'\(([0-9.]*?),\t([0-9.]*?),\t([0-9.]*?)\)\t(.*?)[\t ]*#', chomp(p)).groups() except: print "Couldn't understand" + repr(chomp(p)) continue # Find the area with this cave in area = '' for tryarea in areas: for (number, cave, locn) in cavelists[tryarea]: if name == number: area = tryarea break # FIXME really want to break from both loops at once # but I don't know how to in Python if area != '': break (x,y,d) = map(float, (x,y,d)) for maparea in maps.keys(): lo = mungecoord(x-d, y+d, maparea) hi = mungecoord(x+d, y-d, maparea) lpos = mungecoord(x-d, y, maparea) draw = draws[maparea] draw.ellipse([lo,hi], outline="#000000") draw.ellipse([lo[0]+1, lo[1]+1,hi[0]-1, hi[1]-1], outline=areacolours[area]) draw.ellipse([lo[0]+2, lo[1]+2,hi[0]-2, hi[1]-2], outline=areacolours[area]) draw.rectangle([lpos[0],lpos[1]-SIZE/2, lpos[0] + draw.textsize(name)[0], lpos[1]+SIZE/2], fill="#ffffff") imgmaps[maparea].append( [lpos[0],lpos[1]-SIZE/2, lpos[0] + draw.textsize(name)[0], lpos[1]+SIZE/2, name, "Approx position of %s" % name] ) draw.text((lpos[0], lpos[1]-SIZE/2), name, fill="#000000") plot(positions["laser.0_7"], "BNase", "6", "Bräuning Nase laser point") plot(positions["226-96"], "BZkn", "6", "Bräuning Zinken trig point") plot(positions["vd1"],"VD1","6", "VD1 survey point") plot(positions["laser.kt114_96"],"HSK","6", "Hinterer Schwarzmooskogel trig point") plot(positions["2000"],"Nipple","6", "Nipple (Weiße Warze)") plot(positions["3000"],"VSK","6", "Vorderer Schwarzmooskogel summit") plot(positions["topcamp"], "TC", "6", "Top Camp") plot(positions["laser.0"], "LSR0", "6", "Laser Point 0") plot(positions["laser.0_1"], "LSR1", "6", "Laser Point 0/1") plot(positions["laser.0_3"], "LSR3", "6", "Laser Point 0/3") plot(positions["laser.0_5"], "LSR5", "6", "Laser Point 0/5") plot(positions["225-96"], "BAlm", "6", "Bräuning Alm trig point") for (loctuple, number, area, label) in cavestoplot: plot(loctuple, number, area, label) for area in areas: if area: htmlfile.write("

   %s

" % (areacolours.get(area, "#ffffff"), areanames[area])) else: htmlfile.write("

Location unclear

") htmlfile.write("\n") htmlfile.write("") for (number, cave, locn) in cavelists[area]: # We only check the "oursness" for the first entrance. if cave["Multiple entrances"] in ["", "yes"]: notours = not re.search(r'CUCC', cave['Explorers']) if notours: htmlfile.write("") else: htmlfile.write("") if cave["Autogen file"]: htmlfile.write("" % (cave["Autogen file"], longnumber(cave, number), number.replace("—", ""), cave["Name"] or cave["Unofficial Name"])) else: htmlfile.write("" % (longnumber(cave, number), number.replace("—", ""), cave["Name"] or cave["Unofficial Name"])) htmlfile.write(is_explored(cave)) htmlfile.write(have_survey_data(cave)) htmlfile.write(is_underground_surveyed(cave)) htmlfile.write(is_tagged(cave)) htmlfile.write(has_photo(cave)) htmlfile.write(locn) if(cave["Findability"] != "Surveyed" and cave["Multiple entrances"] != "yes"): htmlfile.write("" % (cave["Location"], cave["Bearings"])) htmlfile.write("\n") htmlfile.write("
Cave NumberNameFinishedSurvey
Data
Survey
Drawn
MarkedPhotoENAltLocation
%s%s%s%s%s %s
\n") for maparea in imgmaps.keys(): writeout_imagemap(imgmaps[maparea], "map" + maparea) htmlfile.write("") htmlfile.close() if showbg: imgpath = "../" else: imgpath = "../../handbook/" for maparea in imgs.keys(): del draws[maparea] filename = get_img_name(maparea) imgs[maparea].save(imgpath + filename) # vim:syntax=python:set ts=4: