# HG changeset patch # User hh # Date 1589784531 -7200 # Node ID bb616224c02a2c94f10ba292850aa1e14fb8cbfd -- diff -r 000000000000 -r bb616224c02a kml/__init__.py diff -r 000000000000 -r bb616224c02a kml/config.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kml/config.py Mon May 18 08:48:51 2020 +0200 @@ -0,0 +1,246 @@ +import json +import os + +import targets +from kml import * + + +class Config: + class Value: + def __init__(self, key, val): + self.key = key + self.val = val + self.chk = True + self.keyact = key + '_actual' + + def extract(self, p): + if self.key in p: + self.val = p[self.key][0] + self.chk = True + + class ValueN(Value): + def extract(self, p): + if self.key in p: + try: + self.val = float(p[self.key][0]) + self.chk = True + except: + self.val = p[self.key][0] + self.chk = False + + def __init__(self): + # ------------------------------------------------- + # zadání - vstupní veličiny + # ------------------------------------------------- + self.name = self.Value('NA', 'flyby') + self.targname = self.Value('TA', '') + self.lat = self.ValueN('LA', targets.Stalin.lat) + self.long = self.ValueN('LO', targets.Stalin.long) + self.h0 = self.ValueN('H0', 300) + self.x0 = self.ValueN('X0', 8000) + self.y0 = self.ValueN('Y0', -12000) + self.z0 = self.ValueN('Z0', 12000) + self.x1 = self.ValueN('X1', 0) + self.y1 = self.ValueN('Y1', -150) + self.z1 = self.ValueN('Z1', 100) + self.exp = self.ValueN('EX', 2) + self.steps = self.ValueN('ST', 600) + self.speedfact = self.ValueN('SPF', 1) + self.slowdown = self.Value('SLD', 'tangential') + self.slowdfact = self.ValueN('SLF', 1) + self.flyby = self.ValueN('FL', 1) + self.influentials = ['name', 'targname', 'lat', 'long', 'h0', 'x0', 'y0', 'z0', + 'x1', 'y1', 'z1', 'exp', 'steps', 'speedfact', 'slowdown', 'slowdfact', 'flyby'] + + # ------------------------------------------------- + # počáteční podmínky + # ------------------------------------------------- + self.dXmin = 0.1 # min delta X při numerické konvergenci + self.minZ = 60 # minimální přípustná výška nad terénem v metrech + self.dHmax = 5 # empirické maximální přípustné otočení pohledu v jednom kroku ve stupních + self.durOpt = 23 # empirické optimum: rychlost otáčení pohledu = 23°/sec + self.stepsMax = 9999 + + # ------------------------------------------------- + # provozní konstanty + # ------------------------------------------------- + self.configsrv = 'kml' + self.dnldsrv = 'flyby' + if 'HTTP_HOST' in os.environ: + h = os.environ['HTTP_HOST'].split('.') + self.srvhost = h[0].lower() + self.srvdomain = '.'.join(h[1:]) + self.dnldqual = '.'.join((self.dnldsrv, self.srvdomain)) + self.configqual = '.'.join((self.configsrv, self.srvdomain)) + _reqPath = os.environ['HTTP_HOST'] + os.environ['REQUEST_URI'].split('?')[0] + _configKeyWord = 'kml' + _dnldKeyWord = 'flyby' + self.configReq = _reqPath.lower().find(_configKeyWord) > -1 + self.dnldReq = _reqPath.lower().find(_dnldKeyWord) > -1 + _reqUrl = '{scheme}://{host}:{port}{path}'.\ + format(scheme=os.environ['REQUEST_SCHEME'], + host=os.environ['HTTP_HOST'], + port=os.environ['SERVER_PORT'], + path=os.environ['REQUEST_URI'].split('?')[0]) + self.dnldUrl = _reqUrl.lower().replace(_configKeyWord, _dnldKeyWord) + deb('self.dnldUrl=%s' % self.dnldUrl) + self.dnldPath = _reqPath.lower().replace(_configKeyWord, _dnldKeyWord) + deb('self.dnldPath=%s' % self.dnldPath) + self.defaultlat = 50.1 + self.defaultlong = 14.3 + self.parmsdir = 'params' + self.targsdir = 'targets' + self.kmlfn = 'flyby' # jméno výstupu - neměnné + self.zipped = False + # zipping se nedá v současnosti používat, protože Google Earth se s ním nechová standardně + # - viz .../memo/googleearth + self.kmlfile = '{}.{}'.format(self.kmlfn, 'kmz' if self.zipped else 'kml') + self.urlfile = 'url.kml' + self.msg = '' + self.flagged = False + + def adjust(self): + self.x0.val = fabs(self.x0.val) + if self.x0.val < self.dXmin: + self.x0.val = 0 + self.x1.val = 0 + if self.z0.val < self.minZ: + self.z0.val = self.minZ + if self.z1.val < self.minZ: + self.z1.val = self.minZ + self.steps.val = int(round(self.steps.val)) + if self.steps.val <= 0: + self.steps.val = 20 + if self.steps.val > self.stepsMax: + self.steps.val = self.stepsMax + self.exp.val = fabs(self.exp.val) + self.flyby.val = int(fabs(self.flyby.val)) + + if self.x0.val > 0: + self.r0 = degrees(atan(self.y0.val / self.x0.val)) # lokální azimut na startu + self.r1 = 90 if self.y1.val < 0 else -90 # lokální azimut v průletu + if self.y1.val == 0: + if self.y0.val == 0: + self.r1 = 90 + else: + self.r1 = 90 * fabs(self.y0.val) / self.y0.val + if self.exp.val <= 1: + self.r1 = -self.r1 + self.h1 = can360(self.h0.val + self.r0 + self.r1) # azimut v průletu + self.h2 = can360(self.h0.val + 2 * (self.h1 - self.h0.val)) # azimut na konci + + dY = self.y1.val - self.y0.val + dZ = self.z1.val - self.z0.val + if dY == 0: + self.A = pi / 2 if dZ < 0 else -pi / 2 + else: + self.A = atan(dZ / dY) # sklon roviny letu, horiz = 0 + if dY > 0: + self.A = self.A + pi if dZ <= 0 else self.A - pi + + F = sqrt(pow(dY, 2) + pow(dZ, 2)) # maximální funkční hodnota + if self.x0.val > 0: + self.K = F / pow(self.x0.val, self.exp.val) # koeficient paraboly + + sa = SpeedAdjust(self.speedfact.val, self.slowdfact.val, self.steps.val, self) + self.speedAdjust = sa.slowdowns[self.slowdown.val](sa) + + self.lastH = self.h0.val + self.lastX, self.lastY, self.lastZ = self.x0.val, self.y0.val, self.z0.val + self.dX = -2 * self.x0.val / self.steps.val + if fabs(self.dX) < self.dXmin: + self.dX = -self.dXmin + self.sumSteps, self.sumDur = 0, 0 + self.rr = [] + + def update_influentials(self, p): + for n in self.influentials: + v = eval('self.{}'.format(n)) + v.extract(p) + self.flagged += (not v.chk) + + v = self.lat + if v.chk: + v.chk = (v.val >= -90 and v.val <= 90) + v = self.long + if v.chk: + v.chk = v.val >= -180 and v.val <= 180 + v = self.slowdown + v.chk = v.val in SpeedAdjust.keys + + self.flagged = False + for n in self.influentials: + v = eval('self.{}'.format(n)) + self.flagged += (not v.chk) + + def parmspath(self): + return os.path.join(self.parmsdir, self.name.val) + + def parmsls(self): + l = os.listdir(self.parmsdir) + l.sort() + #deb("l={}".format(l)) + return l + + def targspath(self, name): + return os.path.join(self.targsdir, name) + + def targsls(self): + l = os.listdir(self.targsdir) + l.sort() + return l + + def gettarget(self, name): + fn = name if name != '' else 'point' + path = self.targspath(fn) + long = self.defaultlong + lat = self.defaultlat + if os.path.exists(path): + g = {} + for p in open(path).read().split(','): + k, v = p.split('=') + g[k] = v + if self.long.key in g: + long = g[self.long.key] + if self.lat.key in g: + lat = g[self.lat.key] + return long, lat + + def storetarget(self, name, long, lat): + fn = name if name != '' else 'point' + path = self.targspath(fn) + open(path, mode='w').write('{}={},{}={}'.format(self.lat.key, lat, self.long.key, long)) + + def restoreparms(self): + if not os.path.exists(self.parmspath()): + return + parms = dict(json.load(open(self.parmspath()))) + # for p in open(self.parmspath()).read().split(','): + # k,v = p.split('=') + # parms[k] = [v] + self.update_influentials(parms) + + def storeparms(self): + if not self.name: + return + # s = [] + p = {} + for n in self.influentials: + v = eval('self.{}'.format(n)) + # s = s + ['='.join((v.key,str(v.val)))] + p[v.key] = [str(v.val)] + # open(self.parmspath(), mode='w').write(','.join(s)) + json.dump(p, open(self.parmspath(), mode='w'), ensure_ascii=False) + + def printparms(self): + s = [] + for n in self.influentials: + v = eval('self.{}'.format(n)) + s = s + ['{}={}'.format(v.key, v.val)] + return '\n'.join(s) + + def adjustenv(self): + p = {} + for k in os.environ: + p[k] = [os.environ[k]] + return p diff -r 000000000000 -r bb616224c02a kml/flyby.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kml/flyby.py Mon May 18 08:48:51 2020 +0200 @@ -0,0 +1,243 @@ +# coding=utf8 + +import targets +import config +from kml import * +import sys +import os +import time +from math import * + +""" +# ----------------------------------------------------------------------------------------------------------- +# pokusy s KML v Googleearth +# ● program generuje položky LookAt tak, aby vznikl pohyb kamery jako symetrický průlet kolem cílového bodu, +# na který kamera po celou dobu letu hledí +# ● cílový bod leží vždy v počátku lokálních souřadnic letu +# ● zadávané souřadnice výchozího bodu a průletu jsou lokální souřadnice (v metrech) vzhledem k cílovému bodu +# ● výchozí bod je X0>=0, Y0, Z0>minZ>0; pro X0=0 se vytváří jen jediný LookAt +# ● bod průletu je X1=0, Y1, Z1>minZ>0 +# ● křivka průletu je symetrická podle roviny Y,Z a je to buď parabola nebo 2 polopřímky s počátkem v průletu +# ● pro Z0=Z1 probíhá let ve vodorovné rovině, pro Y0=Y1 ve svislé rovině rovnoběžné s X +# ● zadávaný exponent paraboly exp>0; pro exp=1 přejde parabola do polopřímek +# ● zadávané geodetické souřadnice cílového bodu a azimut kamery na startu uchycují lokální soustavu do geodetických souřadnic, +# výška cílového bodu je vždy 0 nad terénem +# ● v modulu kml jsou dispozici geo-souřadnice několika konkrétních bodů +# ● zadává se počet kroků (LookAt), které se generují; propočítávají se body z 1.poloviny letu, +# body 2.poloviny jsou symetrické (mají symetrický lokální azimut) +# ● doba přeletu v každém kroku se určuje podle empirických pravidel: +# 1. podle výšky kamery: rychlost v m/s je číselně rovna střední výšce v m +# 2. podle úhlu otočení v kroku: rychlost taková, aby otáčení vycházelo cca 20°/s +# ** volí se nižší hodnota rychlosti z obou pravidel +# ● průběh rychlosti se může zadat jako nelineární se zpomalením v průletu; +# - k dispozici je 5 podtříd SpeedAdjust od konstatní po exponenciální +# - každá třída je parametrizována váhovými koeficienty pro konstatntní a proměnnou složku rychlosti +# ● Googleearth při změnách azimutu mezi dvěma LookAt většími než empirických dHmax nepohybuje kamerou lineárně, +# proto funkce compStep hlídá změnu azimutu v kroku (dH) a přizpůsobuje dX tak, aby dH byl těsně menší než dHmax +# ● pro každý krok se vypočítává lokální azimut "polohy" kamery (Ri), z něhož se určuje geo-azimut "pohledu" kamery +# (lokální azimut 0 leží na kladné poloose X) +# ● lokální azimut v průletu je R1=+/-90°, pro Y1=0 (kamera hledí svisle dolů) se pokládá R1=-90° pro exp>1 jinak 90° +# ● když chci obrátit směr pohybu kamery po dráze, obrátím znaménka Y0 a Y1 +# a výchozí azimut H0 změním na konečný azimut H2; vypočtené hodnoty se vypisují v komentáři na začátku výstupu +# ● zadáním switche "průlet" se řídí vytvoření celého průletu nebo jen letu do největšího přiblížení (bodu průletu) +# ● výstup ve tvaru Tour v KML Documentu jde na sys.stdout +# +# formáty GEO-souřadnic: +# 50.1009147N, 14.4057506E mapy.cz +# 50.765779° 15.050950° google earth +# 50°02'44.9"N 14°25'52.4"E google maps +# 50.045796, 14.431211 +# 36°58′41″N 82°34′37″W wikipedia +# ----------------------------------------------------------------------------------------------------------- +""" +class Flyby(): + k = Kml() + + def __init__(self, config): + self.c = config + + def pr(self, s): + print(s, file=self.k.out) + + def compMove(self): + c = self.c + xi = c.xi + Ri = 0 + if (c.x0.val > 0 and xi == c.x0.val) or c.x0.val == 0: + yi, zi = c.y0.val, c.z0.val + heading = c.h0.val + elif(xi == 0): + yi, zi = c.y1.val, c.z1.val + heading = c.h1 + else: + try: f = c.K * pow(xi, c.exp.val) + except: + deb('ABEND: K={}, xi={}, exp={}'.format(c.K, xi, c.exp.val)) + sys.stderr.close() + os._exit(1) + yi = c.y1.val + f * cos(c.A) + zi = c.z1.val + f * sin(c.A) + Ri = degrees(atan(yi / xi)) + heading = can360(c.h0.val + c.r0 - Ri) + + dist = sqrt(pow(xi,2) + pow(yi,2) + pow(zi,2)) + tilt = degrees(acos(zi / dist)) + + dX = xi - c.lastX + dY = yi - c.lastY + dZ = zi - c.lastZ + dS = sqrt(pow(dX,2) + pow(dY,2) + pow(dZ,2)) + tDur = dS / pow((zi + fabs(dZ/2)), 1) # empirické optimum: rychlost letu = výška/sec + dH = can180(heading - c.lastH) + rDur = fabs(dH) / c.durOpt # empirické optimum: rychlost otáčení + rawdur = rDur if rDur > tDur else tDur + dur = rawdur * c.speedAdjust.koef() if c.x0.val > 0 else rawdur + + # self.drspeed = dS/rawdur if rawdur > 0 else -0.0 # +++ + # self.dspeed = dS/dur if dur > 0 else -0.0 # +++ + # self.dkoef = c.speedAdjust.koef() # +++ + # self.drdur = rDur # +++ + # self.dtdur = tDur # +++ + + c.yi, c.zi = yi, zi + + s = ('Xi={:.3f}, dX={:.3f}, Yi={:.1f}, dY={:.3f}, Zi={:.1f}, dZ={:.3f}, dS={:.3f}, Ri={:.3f}, dist={:.1f}, tilt={:.1f}, ' + .format(xi, dX, yi, dY, zi, dZ, dS, Ri, dist, tilt), + 'heading={:.3f}, dH={:.3f}, dur={:.3f}'.format(heading, dH, dur)) + + return (s, dur, dist, tilt, heading) + + def compStep(self): + c = self.c + (s, dur, dist, tilt, heading) = self.compMove() + dH = can180(heading - c.lastH) + bad = fabs(dH) > c.dHmax + if(bad and fabs(c.dX) > c.dXmin): + ddX = -c.dX/2 + last = bad + found = 0 + while(fabs(dH) > 0 and fabs(dH) != c.dHmax and fabs(ddX) > c.dXmin): + c.dX = c.dX + ddX + c.xi = c.lastX + c.dX + (s, dur, dist, tilt, heading) = self.compMove() + dH = can180(heading - c.lastH) + bad = fabs(dH) > c.dHmax + if(not bad): found = c.xi + ddX = ddX/2 if bad == last else -ddX/2 + last = bad + if(fabs(c.dX) > c.xi): c.dX = -c.xi + if(bad and found > 0): + c.xi = found + s, dur, dist, tilt, heading = self.compMove() + dH = can180(heading - c.lastH) + # deb('Xi={:.3f}, i={:.3f}, koef = {:.3f}, speed={:.3f}, adj speed={:.3f}, trans dur={:.3f}, rot dur={:.3f}' + # .format(c.xi, 1 - c.xi / c.x0.val, self.dkoef, self.drspeed, self.dspeed, self.dtdur, self.drdur)) # +++ + return s, dur, dist, tilt, heading, dH + + def compTour(self): + c = self.c + + c.xi = c.x0.val + s, dur, dist, tilt, heading, dH = self.compStep() + self.fly(s[0]+s[1], 5, False, dist, tilt, heading) + if(c.x0.val == 0): return + + c.xi = c.xi + c.dX + while(c.xi > fabs(c.dX)): + s, dur, dist, tilt, heading, dH = self.compStep() + self.fly(s[0]+s[1], dur, True, dist, tilt, heading) + c.xi = c.xi + c.dX + + self.k.comm('>>> střed') + c.xi = 0 + s, dur, dist, tilt, heading, dH = self.compStep() + if(fabs(dH) <= c.dHmax): self.fly(s[0]+s[1], dur, True, dist, tilt, heading) + while(c.xi > c.dXmin): + self.k.comm('>>> dotažení středu') + c.xi = 0 + s, dur, dist, tilt, heading, dH = self.compStep() + if(fabs(dH) <= c.dHmax): self.fly(s[0]+s[1], dur, True, dist, tilt, heading) + else: break + if(fabs(dH) > c.dHmax): + self.k.comm('>>> dotočení středu') + c.xi = 0 + s, dur, dist, tilt, heading = self.compMove() + d = c.dHmax * dH / fabs(dH) + h = c.lastH + while(fabs(dH) > c.dHmax): + h = can360(h + d) + dur = fabs(d)/c.durOpt + s_ = s[0] + 'heading={:.3f}, dH={:.3f}, dur={:.3f}'.format(h, d, dur) + self.fly(s_, dur, True, dist, tilt, h) + dH = can180(heading - h) + if(fabs(dH) > 0): + dur = fabs(dH)/c.durOpt + s_ = s[0] + 'heading={:.3f}, dH={:.3f}, dur={:.3f}'.format(heading, dH, dur) + self.fly(s_, dur, True, dist, tilt, heading) + + if(c.flyby): + c = self.c + del c.rr[0] # středový záznam se škrtá, neopakuje se + lastH = c.h1 + for i in c.rr: + nDur, dist, tilt, heading = i + heading = can360(2 * c.h1 - heading) # heading je zrcadlový vůči svislé rovině headingu v průletu (H1) + dH = can180(heading - lastH) + s_ = 'dist={:.1f}, tilt={:.1f}, heading={:.3f}, dH={:.3f}, dur={:.3f}'.format(dist, tilt, heading, dH, dur) + self.fly(s_, dur, True, dist, tilt, heading) # duration se bere z předchozího záznamu + dur = nDur + lastH = heading + + self.k.comm('sum: steps={}, duration={:.2f}'.format(c.sumSteps, c.sumDur)) + + def step(self, dur, dist, tilt, heading): + c = self.c + c.lastH = heading + c.lastX, c.lastY, c.lastZ = c.xi, c.yi, c.zi + c.rr = [(dur, dist, tilt, heading)] + c.rr + c.sumDur = c.sumDur + dur + c.sumSteps = c.sumSteps + 1 + + def fly(self, s, dur, smooth, dist, tilt, heading): + self.step(dur, dist, tilt, heading) + self.k.comm(s) + self.k.flyto.head(dur, smooth) + self.k.lookat.run(self.c.long.val, self.c.lat.val, 0, heading, tilt, dist) + # self.k.camera.runL(c.long.val, c.lat.val, 0, heading, tilt, dist, 0) + self.k.flyto.tail() + + def headcomms(self): + c = self.c + self.k.comm(time.asctime()) + c.confcomm = ('lat={lat}, long={long}, heading={heading}, exp={exp}, ' + + 'flyby={flyby}, speedfactor={speedf}, slowdown={slowd}, slowdfactor={slowdf}, steps={steps}')\ + .format(target=c.targname.val, long=c.long.val, lat=c.lat.val, heading=c.h0.val, exp=c.exp.val, + flyby=c.flyby.val, speedf=c.speedfact.val, slowd=c.slowdown.val, slowdf=c.slowdfact.val, steps=c.steps.val) + + if(c.x0.val == 0): + c.coorcomm = 'X0,Y0,Z0={},{},{}, H0={}'.format(c.x0.val, c.y0.val, c.z0.val, c.h0.val) + else: + c.coorcomm = ('X0,Y0,Z0={},{},{}, X1,Y1,Z1={},{},{}, '.format(c.x0.val, c.y0.val, c.z0.val, c.x1.val, c.y1.val, c.z1.val) + + 'A={:.3f}, H0={}, H1={:.3f}, H2={:.3f}, R0={:.3f}, R1={:.3f}' + .format(degrees(c.A), c.h0.val, c.h1, c.h2, c.r0, c.r1)) + + self.k.comm(c.confcomm) + self.k.comm(c.coorcomm) + + def tour(self): + fkml = '{}.kml'.format(int(time.time())) if self.c.zipped else self.c.kmlfile + self.k.out = open(fkml, mode='w', encoding='utf-8') + self.k.xml.head() + self.k.tourProlog(self.c.name.val) + self.headcomms() + self.compTour() + self.k.tourEpilog() + self.k.xml.tail() + self.k.out.close() + if self.c.zipped: + fkmz = '{}.kmz'.format(self.c.kmlfn) + if os.path.exists(fkmz): + os.remove(fkmz) + from subprocess import Popen + p = Popen(['zip', fkmz, fkml], stdout=sys.stderr) + p.wait() diff -r 000000000000 -r bb616224c02a kml/hh_http.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kml/hh_http.py Mon May 18 08:48:51 2020 +0200 @@ -0,0 +1,258 @@ +# coding=utf8 + +import sys +import os +import time +import cgi +import cgitb +import html +from kml import * +from kml import deb +from subprocess import Popen + +class Http(): + titles = {} + titles['general'] = """ +● vytvoří KML-stream z elementů LookAt pro GoogleEarth tak, aby vznikl pohyb kamery jako symetrický průlet kolem cílového bodu, na který kamera po celou dobu letu hledí +● cílový bod leží v počátku lokálních souřadnic letu +● křivka průletu je parabola y = abs(Y1 + x**exp) s počátkem v bodu průletu a je symetrická podle roviny y,z lokálních souřadnic +● zadávané souřadnice výchozího bodu a bodu průletu jsou lokální souřadnice (v metrech) vzhledem k cílovému bodu +● výchozí bod má souřadnice X0>=0, Y0, Z0>{minZ}; pro X0=0 se vytváří jen jediný LookAt (vlastně Placemark) +● bod průletu má souřadnice X1=0, Y1, Z1>{minZ} +● zadávaný exponent paraboly je exp>0; pro exp=1 přejde parabola do polopřímek +● pro Z0=Z1 probíhá let ve vodorovné rovině, pro Y0=Y1 ve svislé rovině rovnoběžné s osou x +● zadávané geodetické souřadnice cílového bodu (ve stupních) a azimut kamery na startu (sever=0) uchycují lokální soustavu do geodetických souřadnic, výška cílového bodu je vždy 0 nad terénem +● průběh rychlosti se může zadat jako nelineární se zpomalením v průletu s několika druhy průběhu +● přepínač "flyby" řídí vytvoření celého průletu nebo jen letu do největšího přiblížení (do bodu průletu) +● konfigurace letů a geo-souřadnice cílových bodů je možno pojmenovat a uložit ("store conf" resp. "store geo") a později vytáhnout do formuláře ("get conf" resp. "get geo") +""" + titles['name'] = 'jméno pro uložení aktuální konfigurace z obrazovky na server {server}' + titles['target'] = 'jméno pro uložení aktuálních geodetických souřadnic cílového bodu z obrazovky na server {server}' + titles['submitstored'] = 'vytvořit KML-stream z konfigurace uložené na serveru {server} pod zadaným jménem' + titles['submitconf'] = 'vytvořit KML-stream z konfigurace vyplněné ve formuláři na obrazovce' + titles['storeconf'] = 'uložit konfiguraci na server {server} pod jménem zadaným v položce name' + titles['getconf'] = 'vytáhnout konfiguraci uloženou na serveru {server} pod zadaným jménem' + titles['storegeo'] = 'uložit souřadnice cílového bodu na server {server} pod jménem zadaným v položce target' + titles['getgeo'] = 'vytáhnout souřadnice cílového bodu uložené na serveru {server} pod jménem zadaným v položce target' + titles['azimuth'] = 'geodetický azimut cílového bodu při pohledu z výchozího bodu x0,y0,z0' + titles['speedfact'] = 'koeficient konstantní složky rychlosti letu >0' + titles['slowfact'] = 'koeficient zpomalení v průletu >0' + titles['slowdown'] = 'průběh zpomalení v průletu' + titles['exponent'] = 'exponent (>0) průletové paraboly x = abs(Y1 + x**exp); pro exp=1 přejde parabola do polopřímek s počátkem v bodu průletu' + titles['flyby'] = 'přepínač průlet/nálet; nálet = let z výchozího bodu do maximálního přiblížení (bodu průletu)' + titles['steps'] = 'počet bodů KML-streamu < {stepsMax}' + titles['xyz0'] = 'lokální souřadnice X0>=0, Y0, Z0>{minZ} v metrech výchozího (krajního) bodu průletové paraboly x = abs(Y1 + x**exp); počátek lokálních souřadnic je v cílovém bodě' + titles['xyz1'] = 'lokální souřadnice X1=0, Y1, Z1>{minZ} bodu průletu kolem cílového bodu po parabole x = abs(Y1 + x**exp); počátek lokálních souřadnic je v cílovém bodě' + titles['lat'] = 'geodetická šířka cílového bodu ve stupních -90<š<90; cílový bod je zároveň počátkem lokálních souřadnic letové paraboly' + titles['long'] = 'geodetická délka cílového bodu ve stupních -180') + print('') + print('') + print('') + print('
'.format(html.escape(self.titles['general']))) + print('
Google Earth parabolic flyby') + print(' 
?
') + print('

') + + def htmltrail(self): + print('') + + def sendform(self, confirm=True): + c = self.c + # cgitb.enable() + # cgi.test() + self.htmlhead() + if(confirm): self.sendconfirm() + + if(c.flagged): + print('

FLAG

') + print('
') + print('' + .format(kaname=c.name.keyact, actualname=c.name.val)) + print('' + .format(katargname=c.targname.keyact, actualtargname=c.targname.val)) + print('' + .format(kaslowdown=c.slowdown.keyact, actualsldn=c.slowdown.val)) + + print('
') + print('' + .format(self.titles['getconf'])) + print('' + .format(self.titles['storeconf'])) + print('' + .format(html.escape(self.titles['submitstored']))) + print('

') + + print('') + print('' + .format(kla=c.lat.key, vla=c.lat.val, lat=self.titles['lat'], flag='' if c.lat.chk else 'flag')) + print('' + .format(klo=c.long.key, vlo=c.long.val, long=self.titles['long'], flag='' if c.long.chk else 'flag')) + print('
') + print('' + .format(self.titles['getgeo'])) + print('' + .format(self.titles['storegeo'])) + print('

') + + print('') + print('' + .format(kx0=c.x0.key, vx0=c.x0.val, xyz0=self.titles['xyz0'], flag='' if c.x0.chk else 'flag')) + print('' + .format(ky0=c.y0.key, vy0=c.y0.val, xyz0=self.titles['xyz0'], flag='' if c.y0.chk else 'flag')) + print('' + .format(kz0=c.z0.key, vz0=c.z0.val, xyz0=self.titles['xyz0'], flag='' if c.z0.chk else 'flag')) + print('') + print('') + print('' + .format(kh0=c.h0.key, vh0=c.h0.val, azimuth=self.titles['azimuth'], flag='' if c.h0.chk else 'flag')) + print('' + .format(ky1=c.y1.key, vy1=c.y1.val, xyz1=self.titles['xyz1'], flag='' if c.y1.chk else 'flag')) + print('' + .format(kz1=c.z1.key, vz1=c.z1.val, xyz1=self.titles['xyz1'], flag='' if c.z1.chk else 'flag')) + print('') + print('') + print('' + .format(kspeedfact=c.speedfact.key, speedfact=c.speedfact.val, tspeedfact=self.titles['speedfact'], flag='' if c.speedfact.chk else 'flag')) + print('' + .format(kslowdown=c.slowdown.key, slowdown='' if not c.slowdown.chk else SpeedAdjust.abrv[c.slowdown.val], + tslowdown=self.titles['slowdown'], flag='' if c.slowdown.chk else 'flag')) + print('' + .format(kslowdfact=c.slowdfact.key, slowdfact=c.slowdfact.val, tslowfact=self.titles['slowfact'], flag='' if c.slowdfact.chk else 'flag')) + print('') + print('') + print('' + .format(kex=c.exp.key, vex=c.exp.val, exponent=self.titles['exponent'], flag='' if c.exp.chk else 'flag')) + print('' + .format(kfl=c.flyby.key, vfl=(1 if c.flyby.val else 0), flyby=self.titles['flyby'])) + print('' + .format(kst=c.steps.key, vst=c.steps.val, steps=self.titles['steps'], flag='' if c.steps.chk else 'flag')) + print('') + print('
') + + print('' + .format(submitconf=self.titles['submitconf'])) + print('' + .format(storeconf=self.titles['storeconf'])) + print('') + for i in SpeedAdjust.keys: + print('
') + self.htmltrail() + + def sendconfirm(self): + print(""" + {}

+
+ Flyby KML is ready to be opened on Google Earth (GE):
+
+
+ + +
+

{conf}
{coor}
+

+ """.format(time.asctime(), self.c.dnldqual, conf=self.c.confcomm, coor=self.c.coorcomm)) + + def httpheader(self, fn): + print('Content-Type: application/vnd.google-earth.kml+xml') + print('Content-Disposition: attachment; filename="{}"'.format(fn)) + print() + + def sendfile(self, fn): + self.httpheader(fn) + sys.stdout.flush() + p = Popen(('cat', fn)) + p.wait() + sys.stdout.close() diff -r 000000000000 -r bb616224c02a kml/kml.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kml/kml.py Mon May 18 08:48:51 2020 +0200 @@ -0,0 +1,263 @@ +import sys +import xml.sax.saxutils +from math import * + +class Kml(): + def __init__(self): + self.out = sys.stdout + self.placemark = Placemark(self) + self.tour = Tour(self) + self.playlist = Playlist(self) + self.flyto = FlyTo(self) + self.lookat = LookAt(self) + self.camera = Camera(self) + self.document = Document(self) + self.folder = Folder(self) + self.xml = Xml(self) + + def documentProlog(self, fn): + self.xml.head() + self.document.head(fn) + + def documentEpilog(self): + self.document.tail() + self.xml.tail() + + def folderProlog(self, fn): + self.folder.head(fn) + + def folderEpilog(self): + self.folder.tail() + + def tourProlog(self, fn): + self.tour.head(fn) + self.playlist.head() + + def tourEpilog(self): + self.playlist.tail() + self.tour.tail() + + def placemarkProlog(self, fn): + self.xml.head() + self.placemark.head(fn) + + def placemarkEpilog(self): + self.placemark.tail() + self.xml.tail() + + def urlPlacemark(self, url, path): + # ref = '{}'.format(qualname, qualname) + ref = '{}'.format(url, path) + p = self.pr + p('') + p('{ref}'.format(ref = xml.sax.saxutils.escape(ref))) + p('') + + def wait(self, dur): + self.pr('\n{}'.format(dur)) + + def comm(self, c): + self.pr('\n'.format(c)) + + def pr(self, s): + print(s, file=self.out) + +class Xml(): + def __init__(self, kml): + self.kml = kml + + def head(self): + self.kml.pr('') + self.kml.pr('') + + def tail(self): + self.kml.pr('\n') + +class Document(): + def __init__(self, kml): + self.kml = kml + + def head(self, fn): + self.kml.pr('') + self.kml.pr('{}'.format(fn)) + self.kml.pr('1') + + def tail(self): + self.kml.pr('\n') + + +class Folder(): + def __init__(self, kml): + self.kml = kml + + def head(self, fn): + self.kml.pr('') + self.kml.pr('{}'.format(fn)) + self.kml.pr('1') + + def tail(self): + self.kml.pr('\n') + +class Tour(): + def __init__(self, kml): + self.kml = kml + + def head(self, fn): + self.kml.pr('\n') + self.kml.pr('{}'.format(fn)) + + def tail(self): + self.kml.pr('\n') + +class Playlist(): + def __init__(self, kml): + self.kml = kml + + def head(self): + self.kml.pr('\n') + + def tail(self): + self.kml.wait(1) + self.kml.pr('\n') + +class FlyTo(): + def __init__(self, kml): + self.kml = kml + + def head(self, dur, smooth): + self.kml.pr('') + self.kml.pr('{}'.format(dur)) + self.kml.pr('{}'.format('smooth' if smooth else 'bounce')) + + def tail(self): + self.kml.pr('\n') + +class LookAt(): + def __init__(self, kml): + self.kml = kml + + def run(self, long,lat,alt,head,tilt,range): + self.kml.pr('\n') + self.kml.pr('{}'.format(lat)) + self.kml.pr('{}'.format(long)) + self.kml.pr('{}'.format(alt)) + self.kml.pr('{}'.format(head)) + self.kml.pr('{}'.format(tilt)) + self.kml.pr('{}'.format(range)) + self.kml.pr('relativeToGround') + self.kml.pr('') + +class Camera(): + def __init__(self, kml): + self.kml = kml + + def run(self,long,lat,alt,head,tilt,roll): + self.kml.pr('') + self.kml.pr('{}'.format(lat)) + self.kml.pr('{}'.format(long)) + self.kml.pr('{}'.format(alt)) + self.kml.pr('{}'.format(head)) + self.kml.pr('{}'.format(tilt)) + self.kml.pr('{}'.format(roll)) + self.kml.pr('relativeToSeaFloor') + self.kml.pr('') + + def runL(self, long, lat, alt, head, tilt, range, roll): + # dostává parametry LookAt, které konvertuje na parametry Camera tak, aby pohled byl stejný + dLO, dLA, Z = l2c(lat, head, tilt, range) + self.run(long + dLO, lat + dLA, Z, head, tilt, roll) + +class Placemark(): + def __init__(self, kml): + self.kml = kml + + def head(self, fn): + self.kml.pr('') + self.kml.pr('{}'.format(fn)) + self.kml.pr('0') + self.kml.pr('#PIN_YELLOW') + + def tail(self): + self.kml.pr('\n') + +class SpeedAdjust(): + keys = ['none', 'linear', 'parabolic', 'tangential', 'exponential'] + abrv = {'none':'none', 'linear':'lin', 'parabolic':'par', 'tangential':'tang', 'exponential':'exp'} + + def __init__(self, const, koef, steps, config): + self.slowdowns = {'none':self.Const, 'linear':self.Linear, 'parabolic':self.Parabolic, 'tangential':self.Tangent, 'exponential':self.Exponential} + self.const = const + self.koef = koef + self.steps = steps + self.c = config + + def phase(self): + return 1 - self.c.xi / self.c.x0.val + + class Const(): + p = 1 # empirická konstanta + def __init__(self, sa): + self.sa = sa + def koef(self): + return self.p / self.sa.const + + class Linear(): + p = 2 # empirická konstanta + def __init__(self, sa): + self.sa = sa + def koef(self): + return self.p * self.sa.koef * self.sa.phase() / self.sa.const + + class Parabolic(): + p = 2 # empirická konstanta + def __init__(self, sa): + self.sa = sa + def koef(self): + return self.p * pow(self.sa.koef * self.sa.phase(), 2) / self.sa.const + + class Tangent(): + p = 1 # empirická konstanta + def __init__(self, sa): + self.sa = sa + def koef(self): + margin = atan(self.sa.koef) # < pi/2 + return (1 + self.p * tan(self.sa.phase() * margin)) / self.sa.const + + class Exponential(): + p = 1 # empirická konstanta + def __init__(self, sa): + self.sa = sa + def koef(self): + return pow(e, self.p * self.sa.koef * self.sa.phase()) / self.sa.const + +def canonGeodetic(a): + if(a > 360 or a < -360): a = a % 360 + if(a > 180): a = a - 360 + if(a < -180): a = a + 360 + return a + +def can360(a360): + if(a360 < 0): a360 = a360 + 360 + elif(a360 > 360): a360 = a360 - 360 + return a360 + +def can180(a180): + if(a180 < -180): a180 = a180 + 360 + elif(a180 > 180): a180 = a180 - 360 + return a180 + +def l2c(lat, head, tilt, range): + RZ = 6378000 + RL = RZ * cos(radians(lat)) + Hc = can360(head + 180) + Z = range * cos(radians(tilt)) + d = sqrt(pow(range,2) - pow(Z,2)) + dLA = degrees(atan(d * cos(radians(Hc)) / RZ)) + dLO = degrees(atan(d * sin(radians(Hc)) / RL)) + return dLO, dLA, Z + +def deb(s): + print('+++ {}'.format(s), file=sys.stderr) diff -r 000000000000 -r bb616224c02a kml/kmlcgi.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kml/kmlcgi.py Mon May 18 08:48:51 2020 +0200 @@ -0,0 +1,30 @@ +#!/usr/bin/python3 +# coding=utf8 + +import sys +import os +import time +import config +import hh_http +import flyby +from kml import deb + +# for s in ('PYTHONIOENCODING', 'LC_ALL'): +# if s in os.environ: +# deb(os.environ[s]) +# for s in os.environ: +# deb('ENV[{}]={}'.format(s, os.environ[s])) + +if(not 'HTTP_HOST' in os.environ): + sys.exit(1) +c = config.Config() +h = hh_http.Http(c) +fl = flyby.Flyby(c) +if c.configReq: # on config request (...kml...) configure tour + if h.parsehttp(''): # syntax OK + fl.tour() # generate Google Earth tour + h.sendform(confirm=True) + else: + h.sendform(confirm=False) +elif c.dnldReq: # on download request (...flyby...) send KML-file + h.sendfile(c.kmlfile) \ No newline at end of file diff -r 000000000000 -r bb616224c02a kml/targets.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kml/targets.py Mon May 18 08:48:51 2020 +0200 @@ -0,0 +1,20 @@ +class Hradčany(): + name = 'Hradčany' + lat = 50.0908833 + long = 14.4005364 + +class Onekotan(): + name = 'Onekotan' + lat = 49.351015 + long = 154.708890 + +class Memento(): + name = 'memento mori' + lat = 50.0947447 + long = 14.4159806 + +class Target(): + def __init__(self,name,lat,long): + self.name = name + self.lat = float(lat) + self.long = float(long) diff -r 000000000000 -r bb616224c02a loxodroma.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/loxodroma.py Mon May 18 08:48:51 2020 +0200 @@ -0,0 +1,66 @@ +#!/usr/bin/python3 +# coding=utf8 + +import sys, math +from kml import * + +fn = "loxodroma" + +long = 0 # výchozí geodetická délka - stupně +longStep = 5 +maxSteps = 9999 +lat = 87 # výchozí geodetická šířka - stupně +minPoleDiff = 1 # minimální vzdálenost od pólu - stupně +R = 6378 * 1000 # zemský poloměr v metrech +azim = 105 +tilt = 60 +range = 4800 * 1000 # metry +speed = 1000 * 1000 # m/s rychlost přesunu pohledu +durMin = 0.1 # secs minimální doba kroku +durMax = 1 # secs maximální doba kroku + +def loxClong(): + azimR = math.radians(azim) + latR = math.radians(lat) + longStepR = math.radians(longStep) + latNR = math.asin(math.tanh(longStepR / math.tan(azimR) + math.atanh(math.sin(latR)))) + latN = math.degrees(latNR) + longN = canonGeodetic(long + longStep) + move = R * math.fabs(latNR - latR) / math.cos(azimR) # délka posunu na povrchu koule v metrech + dur = math.fabs(move) / speed # doba přeletu + if(dur < durMin): dur = durMin + elif(dur > durMax): dur = durMax + return (longN, latN, dur, move) + +def list(): + global long, lat + k = 0 + while((90 - math.fabs(lat)) >= minPoleDiff and k < maxSteps): + (longN, latN, dur, move) = loxClong() + print('{}\t{}\t{}\t{}'.format(k,longN,latN, move)) + long = longN + lat = latN + k = k + 1 + +def run(): + global long, lat + k = 0 + + FlyTo.head(5, False) + LookAt.run(long, lat, 0, azim, tilt, range) + FlyTo.tail() + + while((90 - math.fabs(lat)) >= minPoleDiff and k < maxSteps): + (longN, latN, dur, move) = loxClong() + FlyTo.head(dur, True) + comm('k={}, long={}, lat={}, longN={}, latN={}, move={}'.format(str(k), str(long), str(lat), str(longN), str(latN), str(move))) + k = k + 1 + LookAt.run(longN, latN, 0, azim, tilt, range) + FlyTo.tail() + long = longN + lat = latN + +# list() +tourProlog(fn) +run() +tourEpilog()