#!/usr/bin/env julia

using Luxor

# coordinates are in RA seconds float and Declination seconds float

constellation_names = Dict(
"AND"=> ("Andromeda","Daughter of Cassiopeia"),
"ANT"=> ("Antlia","The Air Pump"),
"APS"=> ("Apus","Bird of Paradise"),
"AQR"=> ("Aquarius","The Water-Bearer"),
"AQL"=> ("Aquila","The Eagle"),
"ARA"=> ("Ara","The Altar"),
"ARI"=> ("Aries","The Ram"),
"AUR"=> ("Auriga","The Charioteer"),
"BOO"=> ("Bootes","The Herdsman"),
"CAE"=> ("Caelum","The Chisel"),
"CAM"=> ("Camelopardalis","The Giraffe"),
"CNC"=> ("Cancer","The Crab"),
"CVN"=> ("Canes Venatici","The Hunting Dogs"),
"CMA"=> ("Canis Major","The Big Dog"),
"CMI"=> ("Canis Minor","The Little Dog"),
"CAP"=> ("Capricornus","The Goat"),
"CAR"=> ("Carina", "The Keel (of Argo)"),
"CAS"=> ("Cassiopeia","The Queen"),
"CEN"=> ("Centaurus","The Centaur"),
"CEP"=> ("Cepheus","The King"),
"CET"=> ("Cetus","The Whale"),
"CHA"=> ("Chamaeleon","The Chameleon"),
"CIR"=> ("Circinus","The Compasses"),
"COL"=> ("Columba","The Dove"),
"COM"=> ("Coma Berenices","Berenice's Hair"),
"CRA"=> ("Corona Australis","The Southern Crown"),
"CRB"=> ("Corona Borealis","The Northern Crown"),
"CRV"=> ("Corvus", "The Crow"),
"CRT"=> ("Crater","The Cup"),
"CRU"=> ("Crux","The Cross"),
"CYG"=> ("Cygnus","The Swan"),
"DEL"=> ("Delphinus","The Dolphin"),
"DOR"=> ("Dorado","The Swordfish"),
"DRA"=> ("Draco","The Dragon"),
"EQU"=> ("Equuleus","The Little Horse"),
"ERI"=> ("Eridanus","The River"),
"FOR"=> ("Fornax","The Furnace"),
"GEM"=> ("Gemini","The Twins"),
"GRU"=> ("Grus","The Crane (bird)"),
"HER"=> ("Hercules","The Son of Zeus"),
"HOR"=> ("Horologium","The Clock"),
"HYA"=> ("Hydra","The Water Snake (female)"),
"HYI"=> ("Hydrus","The Water Snake (male)"),
"IND"=> ("Indus","The Indian (American)"),
"LAC"=> ("Lacerta","The Lizard"),
"LEO"=> ("Leo","The Lion"),
"LMI"=> ("Leo Minor","The Little Lion"),
"LEP"=> ("Lepus","The Hare"),
"LIB"=> ("Libra","The Balance"),
"LUP"=> ("Lupus","The Wolf"),
"LYN"=> ("Lynx","The Lynx"),
"LYR"=> ("Lyra","The Lyre"),
"MEN"=> ("Mensa","The Table"),
"MIC"=> ("Microscopium","The Microscope"),
"MON"=> ("Monoceros","The Unicorn"),
"MUS"=> ("Musca","The Fly"),
"NOR"=> ("Norma","The Square"),
"OCT"=> ("Octans","The Octant"),
"OPH"=> ("Ophiuchus","The Serpent-Bearer"),
"ORI"=> ("Orion","The Hunter"),
"PAV"=> ("Pavo","The Peacock"),
"PEG"=> ("Pegasus","The Winged Horse"),
"PER"=> ("Perseus","Rescuer of Andromeda"),
"PHE"=> ("Phoenix","The Phoenix"),
"PIC"=> ("Pictor","The Painter"),
"PSC"=> ("Pisces","The Fishes"),
"PSA"=> ("Piscis Austrinus", "The Southern Fish"),
"PUP"=> ("Puppis","The Stern (of Argo)"),
"PYX"=> ("Pyxis","The Compass"),
"RET"=> ("Reticulum","The Reticle"),
"SGE"=> ("Sagitta","The Arrow"),
"SGR"=> ("Sagittarius","The Archer"),
"SCO"=> ("Scorpius","The Scorpion"),
"SCL"=> ("Sculptor","The Sculptor"),
"SCT"=> ("Scutum","The Shield"),
"SER"=> ("Serpens","The Serpent"),
"SER1"=> ("Serpens Caput","The Serpent's Head"),
"SER2"=> ("Serpens Cauda","The Serpent's Tail"),
"SEX"=> ("Sextans","The Sextant"),
"TAU"=> ("Taurus","The Bull"),
"TEL"=> ("Telescopium","The Telescope"),
"TRI"=> ("Triangulum","The Triangle"),
"TRA"=> ("Triangulum Australe",   "The Southern Triangle"),
"TUC"=> ("Tucana","The Toucan"),
"UMA"=> ("Ursa Major","The Great Bear"),
"UMI"=> ("Ursa Minor","The Little Bear"),
"VEL"=> ("Vela","The Sails (of Argo)"),
"VIR"=> ("Virgo","The Maiden"),
"VOL"=> ("Volans","The Flying Fish"),
"VUL"=> ("Vulpecula","The Fox")
)

# these are the polygon boundaries of the constellations.
# Some constellations have two polygons, either side of 0h RA.

constellation_boundaries = Dict{Any,Any}(
"NOR"=>Array[Point[
    Point(55200.0,-216000.0),
    Point(59115.0,-216000.0),
    Point(59115.0,-160200.0),
    Point(59115.0,-151200.0),
    Point(57600.0,-151200.0),
    Point(56400.0,-151200.0),
    Point(56400.0,-172800.0),
    Point(55200.0,-172800.0),
    Point(55200.0,-194400.0),
    Point(54180.0,-194400.0),
    Point(54180.0,-198000.0),
    Point(55200.0,-198000.0)]],
"CIR"=>Array[Point[
    Point(48600.0,-230400.0),
    Point(48600.0,-234000.0),
    Point(49200.0,-234000.0),
    Point(49200.0,-252000.0),
    Point(53100.0,-252000.0),
    Point(53100.0,-239400.0),
    Point(53700.0,-239400.0),
    Point(53700.0,-224700.0),
    Point(54600.0,-224700.0),
    Point(54600.0,-219600.0),
    Point(55200.0,-219600.0),
    Point(55200.0,-216000.0),
    Point(55200.0,-198000.0),
    Point(54180.0,-198000.0),
    Point(52320.0,-198000.0),
    Point(52320.0,-230400.0)]],
"CVN"=>Array[Point[
    Point(43200.0,122400.0),
    Point(43200.0,162000.0),
    Point(43500.0,162000.0),
    Point(43500.0,190800.0),
    Point(48600.0,190800.0),
    Point(48600.0,174600.0),
    Point(50520.0,174600.0),
    Point(50520.0,110700.0),
    Point(50250.0,110700.0),
    Point(50250.0,102600.0),
    Point(48600.0,102600.0),
    Point(47700.0,102600.0),
    Point(47700.0,115200.0),
    Point(44400.0,115200.0),
    Point(44400.0,122400.0)]],
"VUL"=>Array[Point[
    Point(67920.0,75900.0),
    Point(67920.0,91800.0),
    Point(69330.0,91800.0),
    Point(69330.0,99000.0),
    Point(70800.0,99000.0),
    Point(70800.0,104400.0),
    Point(75300.0,104400.0),
    Point(75300.0,100800.0),
    Point(77100.0,100800.0),
    Point(77100.0,84600.0),
    Point(76500.0,84600.0),
    Point(76500.0,70200.0),
    Point(75780.0,70200.0),
    Point(74040.0,70200.0),
    Point(74040.0,73800.0),
    Point(72900.0,73800.0),
    Point(72900.0,76500.0),
    Point(71400.0,76500.0),
    Point(71400.0,69000.0),
    Point(69300.0,69000.0),
    Point(69300.0,75900.0)]],
"FOR"=>Array[Point[
    Point(6000.0,-85020.0),
    Point(9540.0,-85020.0),
    Point(13500.0,-85020.0),
    Point(13500.0,-129600.0),
    Point(12600.0,-129600.0),
    Point(12600.0,-138300.0),
    Point(10800.0,-138300.0),
    Point(10800.0,-144000.0),
    Point(8400.0,-144000.0),
    Point(6000.0,-144000.0),
    Point(6000.0,-88200.0)]],
"RET"=>Array[Point[
    Point(11520.0,-239400.0),
    Point(16500.0,-239400.0),
    Point(16500.0,-212400.0),
    Point(15600.0,-212400.0),
    Point(15600.0,-199800.0),
    Point(14400.0,-199800.0),
    Point(14400.0,-190200.0),
    Point(13800.0,-190200.0),
    Point(12600.0,-190200.0),
    Point(12600.0,-203400.0),
    Point(11520.0,-203400.0)]],
"AQL"=>Array[Point[
    Point(66900.0,0.0),
    Point(66900.0,7200.0),
    Point(67920.0,7200.0),
    Point(67920.0,22500.0),
    Point(67184.0,22500.0),
    Point(67184.0,43200.0),
    Point(67920.0,43200.0),
    Point(67920.0,66600.0),
    Point(68400.0,66600.0),
    Point(68400.0,58200.0),
    Point(71400.0,58200.0),
    Point(71400.0,56700.0),
    Point(72510.0,56700.0),
    Point(72510.0,30600.0),
    Point(73080.0,30600.0),
    Point(73080.0,7200.0),
    Point(73920.0,7200.0),
    Point(73920.0,0.0),
    Point(73920.0,-32400.0),
    Point(72000.0,-32400.0),
    Point(72000.0,-43080.0),
    Point(67920.0,-43080.0),
    Point(67920.0,-14400.0),
    Point(66900.0,-14400.0)]],
"UMA"=>Array[Point[
    Point(34500.0,151200.0),
    Point(33000.0,151200.0),
    Point(33000.0,169200.0),
    Point(30300.0,169200.0),
    Point(30300.0,216000.0),
    Point(28680.0,216000.0),
    Point(28680.0,264600.0),
    Point(33000.0,264600.0),
    Point(40800.0,264600.0),
    Point(40800.0,239400.0),
    Point(43200.0,239400.0),
    Point(43200.0,230400.0),
    Point(48600.0,230400.0),
    Point(48600.0,226800.0),
    Point(51900.0,226800.0),
    Point(51900.0,199800.0),
    Point(50520.0,199800.0),
    Point(50520.0,174600.0),
    Point(48600.0,174600.0),
    Point(48600.0,190800.0),
    Point(43500.0,190800.0),
    Point(43500.0,162000.0),
    Point(43200.0,162000.0),
    Point(43200.0,122400.0),
    Point(43200.0,104400.0),
    Point(42720.0,104400.0),
    Point(39600.0,104400.0),
    Point(39600.0,122400.0),
    Point(38820.0,122400.0),
    Point(38820.0,144000.0),
    Point(36600.0,144000.0),
    Point(36600.0,151200.0)]],
"ARI"=>Array[Point[
    Point(7200.0,35700.0),
    Point(6000.0,35700.0),
    Point(6000.0,90000.0),
    Point(6900.0,90000.0),
    Point(6900.0,98100.0),
    Point(8700.0,98100.0),
    Point(8700.0,110400.0),
    Point(9780.0,110400.0),
    Point(12120.0,110400.0),
    Point(12120.0,68400.0),
    Point(11820.0,68400.0),
    Point(11820.0,35700.0)]],
"CRU"=>Array[Point[
    Point(42600.0,-198000.0),
    Point(42600.0,-230400.0),
    Point(46200.0,-230400.0),
    Point(46200.0,-198000.0)]],
"PHE"=>Array[Point[
    Point(0.0,-207000.0),
    Point(4800.0,-207000.0),
    Point(4800.0,-189000.0),
    Point(5700.0,-189000.0),
    Point(5700.0,-181800.0),
    Point(6600.0,-181800.0),
    Point(6600.0,-172200.0),
    Point(8400.0,-172200.0),
    Point(8400.0,-144000.0),
    Point(0.0,-144000.0)],
    Point[
    Point(86400.0,-144000.0),
    Point(84000.0,-144000.0),
    Point(84000.0,-205200.0),
    Point(86400.0,-205200.0)]],
"AND"=>Array[Point[
    Point(0.0,172800.0),
    Point(0.0,165600.0),
    Point(3120.0,165600.0),
    Point(3120.0,172800.0),
    Point(4020.0,172800.0),
    Point(4020.0,180000.0),
    Point(4920.0,180000.0),
    Point(6000.0,180000.0),
    Point(6000.0,169200.0),
    Point(7350.0,169200.0),
    Point(7350.0,181800.0),
    Point(9060.0,181800.0),
    Point(9060.0,132300.0),
    Point(7200.0,132300.0),
    Point(7200.0,126000.0),
    Point(5070.0,126000.0),
    Point(5070.0,118800.0),
    Point(2580.0,118800.0),
    Point(2580.0,85500.0),
    Point(3060.0,85500.0),
    Point(3060.0,75600.0),
    Point(510.0,75600.0),
    Point(510.0,79200.0),
    Point(240.0,79200.0),
    Point(240.0,100800.0),
    Point(0.0,100800.0),
    Point(0.0,112800.0)],
    Point[
    Point(82320.0,124200.0),
    Point(82320.0,189000.0),
    Point(84000.0,189000.0),
    Point(84000.0,180000.0),
    Point(84900.0,180000.0),
    Point(84900.0,172800.0),
    Point(86400.0,172800.0),
    Point(86400.0,112800.0),
    Point(85500.0,112800.0),
    Point(85500.0,115500.0),
    Point(84600.0,115500.0),
    Point(84600.0,124200.0)
    ]],
"CRB"=>Array[Point[
    Point(54660.0,93600.0),
    Point(54660.0,118800.0),
    Point(55560.0,118800.0),
    Point(55560.0,144000.0),
    Point(56700.0,144000.0),
    Point(58800.0,144000.0),
    Point(58800.0,97200.0),
    Point(58200.0,97200.0),
    Point(58200.0,93600.0),
    Point(57720.0,93600.0)]],
"TRA"=>Array[Point[
    Point(53100.0,-252000.0),
    Point(53100.0,-239400.0),
    Point(53700.0,-239400.0),
    Point(53700.0,-224700.0),
    Point(54600.0,-224700.0),
    Point(54600.0,-219600.0),
    Point(55200.0,-219600.0),
    Point(55200.0,-216000.0),
    Point(59115.0,-216000.0),
    Point(59115.0,-219600.0),
    Point(59700.0,-219600.0),
    Point(59700.0,-224700.0),
    Point(60300.0,-224700.0),
    Point(60300.0,-234000.0),
    Point(60600.0,-234000.0),
    Point(60600.0,-239400.0),
    Point(61200.0,-239400.0),
    Point(61200.0,-252000.0)]],
"SCT"=>Array[Point[
    Point(65700.0,-57600.0),
    Point(67920.0,-57600.0),
    Point(67920.0,-43080.0),
    Point(67920.0,-14400.0),
    Point(66900.0,-14400.0),
    Point(65700.0,-14400.0)]],
"HOR"=>Array[Point[
    Point(15360.0,-144000.0),
    Point(15360.0,-176400.0),
    Point(14700.0,-176400.0),
    Point(14700.0,-183600.0),
    Point(13800.0,-183600.0),
    Point(13800.0,-190200.0),
    Point(12600.0,-190200.0),
    Point(12600.0,-203400.0),
    Point(11520.0,-203400.0),
    Point(11520.0,-239400.0),
    Point(7800.0,-239400.0),
    Point(7800.0,-207000.0),
    Point(7800.0,-194400.0),
    Point(8700.0,-194400.0),
    Point(8700.0,-183600.0),
    Point(9600.0,-183600.0),
    Point(9600.0,-176400.0),
    Point(10800.0,-176400.0),
    Point(10800.0,-165600.0),
    Point(12300.0,-165600.0),
    Point(12300.0,-158400.0),
    Point(13920.0,-158400.0),
    Point(13920.0,-144000.0)]],
"VEL"=>Array[Point[
    Point(39600.0,-199800.0),
    Point(39600.0,-137700.0),
    Point(33720.0,-137700.0),
    Point(33720.0,-126900.0),
    Point(30120.0,-126900.0),
    Point(30120.0,-154800.0),
    Point(28800.0,-154800.0),
    Point(28800.0,-177300.0),
    Point(29400.0,-177300.0),
    Point(29400.0,-190800.0),
    Point(30420.0,-190800.0),
    Point(30420.0,-192600.0),
    Point(31800.0,-192600.0),
    Point(31800.0,-199800.0)]],
"PIC"=>Array[Point[
    Point(21600.0,-154800.0),
    Point(18000.0,-154800.0),
    Point(17400.0,-154800.0),
    Point(17400.0,-163800.0),
    Point(16200.0,-163800.0),
    Point(16200.0,-176400.0),
    Point(16200.0,-194400.0),
    Point(18000.0,-194400.0),
    Point(18000.0,-203400.0),
    Point(19800.0,-203400.0),
    Point(19800.0,-219600.0),
    Point(21600.0,-219600.0),
    Point(21600.0,-230400.0),
    Point(23700.0,-230400.0),
    Point(24600.0,-230400.0),
    Point(24600.0,-208800.0),
    Point(23400.0,-208800.0),
    Point(23400.0,-198000.0),
    Point(22200.0,-198000.0),
    Point(22200.0,-185400.0),
    Point(21600.0,-185400.0),
    Point(21600.0,-177300.0)]],
"MEN"=>Array[Point[
    Point(27600.0,-307800.0),
    Point(12600.0,-307800.0),
    Point(12600.0,-270000.0),
    Point(16500.0,-270000.0),
    Point(16500.0,-252000.0),
    Point(23700.0,-252000.0),
    Point(23700.0,-270000.0),
    Point(27600.0,-270000.0),
    Point(27600.0,-307800.0)]],
"HER"=>Array[Point[
    Point(58560.0,14400.0),
    Point(57900.0,14400.0),
    Point(57900.0,57600.0),
    Point(57300.0,57600.0),
    Point(57300.0,79200.0),
    Point(57720.0,79200.0),
    Point(57720.0,93600.0),
    Point(58200.0,93600.0),
    Point(58200.0,97200.0),
    Point(58800.0,97200.0),
    Point(58800.0,144000.0),
    Point(56700.0,144000.0),
    Point(56700.0,185400.0),
    Point(61200.0,185400.0),
    Point(61200.0,181800.0),
    Point(65640.0,181800.0),
    Point(65640.0,171000.0),
    Point(65430.0,171000.0),
    Point(65430.0,108000.0),
    Point(66120.0,108000.0),
    Point(66120.0,93600.0),
    Point(67920.0,93600.0),
    Point(67920.0,91800.0),
    Point(67920.0,75900.0),
    Point(67920.0,66600.0),
    Point(67920.0,43200.0),
    Point(67184.0,43200.0),
    Point(65700.0,43200.0),
    Point(65700.0,51600.0),
    Point(62100.0,51600.0),
    Point(62100.0,46200.0),
    Point(60300.0,46200.0),
    Point(60300.0,14400.0)]],
"LUP"=>Array[Point[
    Point(51000.0,-198000.0),
    Point(52320.0,-198000.0),
    Point(54180.0,-198000.0),
    Point(54180.0,-194400.0),
    Point(55200.0,-194400.0),
    Point(55200.0,-172800.0),
    Point(56400.0,-172800.0),
    Point(56400.0,-151200.0),
    Point(57600.0,-151200.0),
    Point(57600.0,-102600.0),
    Point(56400.0,-102600.0),
    Point(53700.0,-102600.0),
    Point(53700.0,-151200.0),
    Point(51000.0,-151200.0)]],
"LIB"=>Array[Point[
    Point(54300.0,0.0),
    Point(52800.0,0.0),
    Point(52800.0,-28800.0),
    Point(51300.0,-28800.0),
    Point(51300.0,-79200.0),
    Point(51300.0,-84600.0),
    Point(53700.0,-84600.0),
    Point(53700.0,-102600.0),
    Point(56400.0,-102600.0),
    Point(56400.0,-72000.0),
    Point(57300.0,-72000.0),
    Point(57300.0,-28800.0),
    Point(57300.0,-9900.0),
    Point(54300.0,-9900.0)]],
"MON"=>Array[Point[
    Point(22470.0,0.0),
    Point(22470.0,36000.0),
    Point(22710.0,36000.0),
    Point(22710.0,43200.0),
    Point(24960.0,43200.0),
    Point(24960.0,36000.0),
    Point(25200.0,36000.0),
    Point(25200.0,19800.0),
    Point(25260.0,19800.0),
    Point(25260.0,5400.0),
    Point(25920.0,5400.0),
    Point(25920.0,0.0),
    Point(29100.0,0.0),
    Point(29100.0,-39600.0),
    Point(26520.0,-39600.0),
    Point(22020.0,-39600.0),
    Point(21000.0,-39600.0),
    Point(21000.0,-14400.0),
    Point(22470.0,-14400.0)]],
"TEL"=>Array[Point[
    Point(73200.0,-205200.0),
    Point(73200.0,-160200.0),
    Point(69000.0,-160200.0),
    Point(64800.0,-160200.0),
    Point(64800.0,-205200.0)]],
"DEL"=>Array[Point[
    Point(73920.0,7200.0),
    Point(73080.0,7200.0),
    Point(73080.0,30600.0),
    Point(72510.0,30600.0),
    Point(72510.0,56700.0),
    Point(72900.0,56700.0),
    Point(72900.0,73800.0),
    Point(74040.0,73800.0),
    Point(74040.0,70200.0),
    Point(75780.0,70200.0),
    Point(75780.0,42600.0),
    Point(75150.0,42600.0),
    Point(75150.0,21600.0),
    Point(75000.0,21600.0),
    Point(75000.0,7200.0)]],
"CEN"=>Array[Point[
    Point(39600.0,-126000.0),
    Point(39600.0,-137700.0),
    Point(39600.0,-199800.0),
    Point(40500.0,-199800.0),
    Point(40500.0,-230400.0),
    Point(42600.0,-230400.0),
    Point(42600.0,-198000.0),
    Point(46200.0,-198000.0),
    Point(46200.0,-230400.0),
    Point(48600.0,-230400.0),
    Point(52320.0,-230400.0),
    Point(52320.0,-198000.0),
    Point(51000.0,-198000.0),
    Point(51000.0,-151200.0),
    Point(53700.0,-151200.0),
    Point(53700.0,-102600.0),
    Point(45300.0,-102600.0),
    Point(45300.0,-118800.0),
    Point(44100.0,-118800.0),
    Point(44100.0,-126000.0)]],
"PAV"=>Array[Point[
    Point(64800.0,-270000.0),
    Point(76800.0,-270000.0),
    Point(76800.0,-216000.0),
    Point(73200.0,-216000.0),
    Point(73200.0,-205200.0),
    Point(64800.0,-205200.0),
    Point(63000.0,-205200.0),
    Point(63000.0,-239400.0),
    Point(64800.0,-239400.0)]],
"SEX"=>Array[Point[
    Point(34500.0,0.0),
    Point(34500.0,25200.0),
    Point(38700.0,25200.0),
    Point(38700.0,0.0),
    Point(38700.0,-21600.0),
    Point(38700.0,-39600.0),
    Point(34500.0,-39600.0)]],
"CAS"=>Array[Point[
    Point(1200.0,237600.0),
    Point(1200.0,277200.0),
    Point(12300.0,277200.0),
    Point(12300.0,244800.0),
    Point(11160.0,244800.0),
    Point(11160.0,205200.0),
    Point(8760.0,205200.0),
    Point(8760.0,210600.0),
    Point(6870.0,210600.0),
    Point(6870.0,207000.0),
    Point(6120.0,207000.0),
    Point(6120.0,194400.0),
    Point(4920.0,194400.0),
    Point(4920.0,180000.0),
    Point(4020.0,180000.0),
    Point(4020.0,172800.0),
    Point(3120.0,172800.0),
    Point(3120.0,165600.0),
    Point(0.0,165600.0),
    Point(0.0,172800.0),
    Point(0.0,237600.0)],
    Point[
    Point(84000.0,189000.0),
    Point(84000.0,180000.0),
    Point(84900.0,180000.0),
    Point(84900.0,172800.0),
    Point(86400.0,172800.0),
    Point(86400.0,226800.0),
    Point(83400.0,226800.0),
    Point(83400.0,212700.0),
    Point(82320.0,212700.0),
    Point(82320.0,189000.0)
    ]],
"CNC"=>Array[Point[
    Point(33300.0,25200.0),
    Point(29100.0,25200.0),
    Point(28530.0,25200.0),
    Point(28530.0,36000.0),
    Point(28110.0,36000.0),
    Point(28110.0,48600.0),
    Point(28110.0,72000.0),
    Point(28380.0,72000.0),
    Point(28380.0,100800.0),
    Point(28800.0,100800.0),
    Point(28800.0,120600.0),
    Point(33300.0,120600.0)]],
"CRA"=>Array[Point[
    Point(64200.0,-133200.0),
    Point(69000.0,-133200.0),
    Point(69000.0,-160200.0),
    Point(64800.0,-160200.0),
    Point(64200.0,-160200.0)]],
"ORI"=>Array[Point[
    Point(16620.0,0.0),
    Point(16620.0,55800.0),
    Point(17880.0,55800.0),
    Point(17880.0,57600.0),
    Point(19200.0,57600.0),
    Point(19200.0,55800.0),
    Point(20160.0,55800.0),
    Point(20160.0,45000.0),
    Point(20760.0,45000.0),
    Point(20760.0,64800.0),
    Point(20520.0,64800.0),
    Point(20520.0,82200.0),
    Point(21180.0,82200.0),
    Point(21180.0,77400.0),
    Point(22380.0,77400.0),
    Point(22380.0,63000.0),
    Point(22710.0,63000.0),
    Point(22710.0,43200.0),
    Point(22710.0,36000.0),
    Point(22470.0,36000.0),
    Point(22470.0,0.0),
    Point(22470.0,-14400.0),
    Point(21000.0,-14400.0),
    Point(21000.0,-39600.0),
    Point(18300.0,-39600.0),
    Point(18300.0,-14400.0),
    Point(16800.0,-14400.0),
    Point(16800.0,0.0)]],
"MUS"=>Array[Point[
    Point(40500.0,-230400.0),
    Point(40500.0,-270000.0),
    Point(49200.0,-270000.0),
    Point(49200.0,-252000.0),
    Point(49200.0,-234000.0),
    Point(48600.0,-234000.0),
    Point(48600.0,-230400.0),
    Point(46200.0,-230400.0),
    Point(42600.0,-230400.0)]],
"TAU"=>Array[Point[
    Point(11820.0,-900.0),
    Point(11820.0,0.0),
    Point(11820.0,35700.0),
    Point(11820.0,68400.0),
    Point(12120.0,68400.0),
    Point(12120.0,110400.0),
    Point(16200.0,110400.0),
    Point(16200.0,108000.0),
    Point(17100.0,108000.0),
    Point(17100.0,102600.0),
    Point(21180.0,102600.0),
    Point(21180.0,100800.0),
    Point(21180.0,82200.0),
    Point(20520.0,82200.0),
    Point(20520.0,64800.0),
    Point(20760.0,64800.0),
    Point(20760.0,45000.0),
    Point(20160.0,45000.0),
    Point(20160.0,55800.0),
    Point(19200.0,55800.0),
    Point(19200.0,57600.0),
    Point(17880.0,57600.0),
    Point(17880.0,55800.0),
    Point(16620.0,55800.0),
    Point(16620.0,0.0),
    Point(12900.0,0.0),
    Point(12900.0,-900.0)]],
"TUC"=>Array[Point[
    Point(0.0,-270000.0),
    Point(2700.0,-270000.0),
    Point(2700.0,-273600.0),
    Point(4800.0,-273600.0),
    Point(4800.0,-207000.0),
    Point(0.0,-207000.0)],
    Point[
    Point(86400.0,-270000.0),
    Point(84000.0,-270000.0),
    Point(84000.0,-239400.0),
    Point(79200.0,-239400.0),
    Point(79200.0,-205200.0),
    Point(86400.0,-205200.0)
    ]],
"APS"=>Array[Point[
    Point(49200.0,-293400.0),
    Point(64800.0,-293400.0),
    Point(64800.0,-270000.0),
    Point(64800.0,-239400.0),
    Point(63000.0,-239400.0),
    Point(61200.0,-239400.0),
    Point(61200.0,-252000.0),
    Point(53100.0,-252000.0),
    Point(49200.0,-252000.0),
    Point(49200.0,-270000.0)]],
"PER"=>Array[Point[
    Point(9780.0,110400.0),
    Point(9780.0,122400.0),
    Point(9240.0,122400.0),
    Point(9240.0,132300.0),
    Point(9060.0,132300.0),
    Point(9060.0,181800.0),
    Point(7350.0,181800.0),
    Point(7350.0,169200.0),
    Point(6000.0,169200.0),
    Point(6000.0,180000.0),
    Point(4920.0,180000.0),
    Point(4920.0,194400.0),
    Point(6120.0,194400.0),
    Point(6120.0,207000.0),
    Point(6870.0,207000.0),
    Point(6870.0,210600.0),
    Point(8760.0,210600.0),
    Point(8760.0,205200.0),
    Point(11160.0,205200.0),
    Point(11400.0,205200.0),
    Point(11400.0,198000.0),
    Point(12000.0,198000.0),
    Point(12000.0,189000.0),
    Point(16890.0,189000.0),
    Point(16890.0,129600.0),
    Point(16200.0,129600.0),
    Point(16200.0,110400.0),
    Point(12120.0,110400.0)]],
"ANT"=>Array[Point[
    Point(33720.0,-86400.0),
    Point(33720.0,-126900.0),
    Point(33720.0,-137700.0),
    Point(39600.0,-137700.0),
    Point(39600.0,-126000.0),
    Point(39000.0,-126000.0),
    Point(39000.0,-111000.0),
    Point(38100.0,-111000.0),
    Point(38100.0,-103800.0),
    Point(36900.0,-103800.0),
    Point(36900.0,-91800.0),
    Point(35100.0,-91800.0),
    Point(35100.0,-86400.0)]],
"SGE"=>Array[Point[
    Point(67920.0,66600.0),
    Point(67920.0,75900.0),
    Point(69300.0,75900.0),
    Point(69300.0,69000.0),
    Point(71400.0,69000.0),
    Point(71400.0,76500.0),
    Point(72900.0,76500.0),
    Point(72900.0,73800.0),
    Point(72900.0,56700.0),
    Point(72510.0,56700.0),
    Point(71400.0,56700.0),
    Point(71400.0,58200.0),
    Point(68400.0,58200.0),
    Point(68400.0,66600.0)]],
"LAC"=>Array[Point[
    Point(78750.0,129600.0),
    Point(78750.0,157500.0),
    Point(78870.0,157500.0),
    Point(78870.0,158400.0),
    Point(79080.0,158400.0),
    Point(79080.0,189900.0),
    Point(79680.0,189900.0),
    Point(79680.0,198000.0),
    Point(80340.0,198000.0),
    Point(80340.0,202500.0),
    Point(82320.0,202500.0),
    Point(82320.0,189000.0),
    Point(82320.0,124200.0),
    Point(82140.0,124200.0),
    Point(82140.0,126000.0),
    Point(79200.0,126000.0),
    Point(79200.0,129600.0)]],
"SER2"=>Array[Point[
    Point(65700.0,0.0),
    Point(65700.0,10800.0),
    Point(66330.0,10800.0),
    Point(66330.0,16200.0),
    Point(65700.0,16200.0),
    Point(65700.0,22500.0),
    Point(67184.0,22500.0),
    Point(67920.0,22500.0),
    Point(67920.0,7200.0),
    Point(66900.0,7200.0),
    Point(66900.0,0.0),
    Point(66900.0,-14400.0),
    Point(65700.0,-14400.0),
    Point(65700.0,-57600.0),
    Point(63360.0,-57600.0),
    Point(61800.0,-57600.0),
    Point(61800.0,-36000.0),
    Point(63300.0,-36000.0),
    Point(63300.0,-37200.0),
    Point(63600.0,-37200.0),
    Point(63600.0,-36000.0),
    Point(64680.0,-36000.0),
    Point(64680.0,-14400.0),
    Point(64200.0,-14400.0),
    Point(64200.0,0.0)]],
"SGR"=>Array[Point[
    Point(67920.0,-43080.0),
    Point(67920.0,-57600.0),
    Point(65700.0,-57600.0),
    Point(63360.0,-57600.0),
    Point(63360.0,-108000.0),
    Point(64200.0,-108000.0),
    Point(64200.0,-133200.0),
    Point(69000.0,-133200.0),
    Point(69000.0,-160200.0),
    Point(73200.0,-160200.0),
    Point(73200.0,-100800.0),
    Point(72000.0,-100800.0),
    Point(72000.0,-43080.0)]],
"AUR"=>Array[Point[
    Point(16200.0,110400.0),
    Point(16200.0,129600.0),
    Point(16890.0,129600.0),
    Point(16890.0,189000.0),
    Point(18000.0,189000.0),
    Point(18000.0,201600.0),
    Point(21960.0,201600.0),
    Point(21960.0,194400.0),
    Point(23400.0,194400.0),
    Point(23400.0,180000.0),
    Point(24480.0,180000.0),
    Point(24480.0,160200.0),
    Point(26520.0,160200.0),
    Point(26520.0,127800.0),
    Point(23520.0,127800.0),
    Point(23520.0,100800.0),
    Point(21180.0,100800.0),
    Point(21180.0,102600.0),
    Point(17100.0,102600.0),
    Point(17100.0,108000.0),
    Point(16200.0,108000.0)]],
"SCL"=>Array[Point[
    Point(82800.0,-88200.0),
    Point(85800.0,-88200.0),
    Point(85800.0,-144000.0),
    Point(84000.0,-144000.0),
    Point(84000.0,-133200.0),
    Point(82800.0,-133200.0)],
    Point[
    Point(6000.0,-88200.0),
    Point(6000.0,-144000.0),
    Point(0.0,-144000.0),
    Point(0.0,-88200.0)]],
"LMI"=>Array[Point[
    Point(33300.0,120600.0),
    Point(33300.0,143100.0),
    Point(34500.0,143100.0),
    Point(34500.0,151200.0),
    Point(36600.0,151200.0),
    Point(36600.0,144000.0),
    Point(38820.0,144000.0),
    Point(38820.0,122400.0),
    Point(39600.0,122400.0),
    Point(39600.0,104400.0),
    Point(39600.0,91800.0),
    Point(38700.0,91800.0),
    Point(38700.0,84600.0),
    Point(37800.0,84600.0),
    Point(37800.0,102600.0),
    Point(35580.0,102600.0),
    Point(35580.0,120600.0)]],
"COL"=>Array[Point[
    Point(18000.0,-154800.0),
    Point(18000.0,-96300.0),
    Point(22020.0,-96300.0),
    Point(22020.0,-118800.0),
    Point(23700.0,-118800.0),
    Point(23700.0,-154800.0),
    Point(21600.0,-154800.0)]],
"SER1"=>Array[Point[
    Point(54300.0,0.0),
    Point(54300.0,28800.0),
    Point(54300.0,93600.0),
    Point(54660.0,93600.0),
    Point(57720.0,93600.0),
    Point(57720.0,79200.0),
    Point(57300.0,79200.0),
    Point(57300.0,57600.0),
    Point(57900.0,57600.0),
    Point(57900.0,14400.0),
    Point(58560.0,14400.0),
    Point(58560.0,0.0),
    Point(58560.0,-9900.0),
    Point(57300.0,-9900.0),
    Point(54300.0,-9900.0)]],
"CRV"=>Array[Point[
    Point(46200.0,-39600.0),
    Point(42600.0,-39600.0),
    Point(42600.0,-84600.0),
    Point(45300.0,-84600.0),
    Point(45300.0,-79200.0),
    Point(46200.0,-79200.0)]],
"IND"=>Array[Point[
    Point(76800.0,-270000.0),
    Point(84000.0,-270000.0),
    Point(84000.0,-239400.0),
    Point(79200.0,-239400.0),
    Point(79200.0,-205200.0),
    Point(79200.0,-180000.0),
    Point(76800.0,-180000.0),
    Point(76800.0,-160200.0),
    Point(73200.0,-160200.0),
    Point(73200.0,-205200.0),
    Point(73200.0,-216000.0),
    Point(76800.0,-216000.0)]],
"MIC"=>Array[Point[
    Point(73200.0,-100800.0),
    Point(76800.0,-100800.0),
    Point(76800.0,-133200.0),
    Point(76800.0,-160200.0),
    Point(73200.0,-160200.0)]],
"SCO"=>Array[Point[
    Point(57300.0,-28800.0),
    Point(58560.0,-28800.0),
    Point(58560.0,-63900.0),
    Point(58950.0,-63900.0),
    Point(58950.0,-67500.0),
    Point(58560.0,-67500.0),
    Point(58560.0,-84300.0),
    Point(60300.0,-84300.0),
    Point(60300.0,-108000.0),
    Point(63360.0,-108000.0),
    Point(64200.0,-108000.0),
    Point(64200.0,-133200.0),
    Point(64200.0,-160200.0),
    Point(59115.0,-160200.0),
    Point(59115.0,-151200.0),
    Point(57600.0,-151200.0),
    Point(57600.0,-102600.0),
    Point(56400.0,-102600.0),
    Point(56400.0,-72000.0),
    Point(57300.0,-72000.0)]],
"CET"=>Array[Point[
    Point(0.0,-25200.0),
    Point(1200.0,-25200.0),
    Point(1200.0,7200.0),
    Point(7200.0,7200.0),
    Point(7200.0,35700.0),
    Point(11820.0,35700.0),
    Point(11820.0,0.0),
    Point(11820.0,-900.0),
    Point(9540.0,-900.0),
    Point(9540.0,-85020.0),
    Point(6000.0,-85020.0),
    Point(6000.0,-88200.0),
    Point(0.0,-88200.0)],

    Point[
    Point(86400.0, -144000.0),
    Point(85800.0, -144000.0),
    Point(85800.0, -25200.0),
    Point(86400.0, -25200.0)
    ]],
"GRU"=>Array[Point[
    Point(76800.0,-133200.0),
    Point(76800.0,-160200.0),
    Point(76800.0,-180000.0),
    Point(79200.0,-180000.0),
    Point(79200.0,-205200.0),
    Point(84000.0,-205200.0),
    Point(84000.0,-144000.0),
    Point(84000.0,-133200.0),
    Point(82800.0,-133200.0)]],
"LEO"=>Array[Point[
    Point(38700.0,0.0),
    Point(38700.0,25200.0),
    Point(34500.0,25200.0),
    Point(33300.0,25200.0),
    Point(33300.0,120600.0),
    Point(35580.0,120600.0),
    Point(35580.0,102600.0),
    Point(37800.0,102600.0),
    Point(37800.0,84600.0),
    Point(38700.0,84600.0),
    Point(38700.0,91800.0),
    Point(39600.0,91800.0),
    Point(39600.0,104400.0),
    Point(42720.0,104400.0),
    Point(42720.0,50400.0),
    Point(42720.0,39600.0),
    Point(41460.0,39600.0),
    Point(41460.0,0.0),
    Point(41460.0,-21600.0),
    Point(38700.0,-21600.0)]],
"PUP"=>Array[Point[
    Point(26520.0,-39600.0),
    Point(26520.0,-118800.0),
    Point(23700.0,-118800.0),
    Point(23700.0,-154800.0),
    Point(21600.0,-154800.0),
    Point(21600.0,-177300.0),
    Point(28800.0,-177300.0),
    Point(28800.0,-154800.0),
    Point(30120.0,-154800.0),
    Point(30120.0,-126900.0),
    Point(30120.0,-61200.0),
    Point(30120.0,-39600.0),
    Point(29100.0,-39600.0)]],
"CMI"=>Array[Point[
    Point(29100.0,0.0),
    Point(25920.0,0.0),
    Point(25920.0,5400.0),
    Point(25260.0,5400.0),
    Point(25260.0,19800.0),
    Point(25200.0,19800.0),
    Point(25200.0,36000.0),
    Point(25200.0,45000.0),
    Point(27000.0,45000.0),
    Point(27000.0,48600.0),
    Point(28110.0,48600.0),
    Point(28110.0,36000.0),
    Point(28530.0,36000.0),
    Point(28530.0,25200.0),
    Point(29100.0,25200.0)]],
"ERI"=>Array[Point[
    Point(12900.0,0.0),
    Point(16620.0,0.0),
    Point(16800.0,0.0),
    Point(16800.0,-14400.0),
    Point(18300.0,-14400.0),
    Point(18300.0,-39600.0),
    Point(17700.0,-39600.0),
    Point(17700.0,-48600.0),
    Point(17400.0,-48600.0),
    Point(17400.0,-96300.0),
    Point(16920.0,-96300.0),
    Point(16920.0,-108000.0),
    Point(16500.0,-108000.0),
    Point(16500.0,-133200.0),
    Point(15360.0,-133200.0),
    Point(15360.0,-144000.0),
    Point(13920.0,-144000.0),
    Point(13920.0,-158400.0),
    Point(12300.0,-158400.0),
    Point(12300.0,-165600.0),
    Point(10800.0,-165600.0),
    Point(10800.0,-176400.0),
    Point(9600.0,-176400.0),
    Point(9600.0,-183600.0),
    Point(8700.0,-183600.0),
    Point(8700.0,-194400.0),
    Point(7800.0,-194400.0),
    Point(7800.0,-207000.0),
    Point(4800.0,-207000.0),
    Point(4800.0,-189000.0),
    Point(5700.0,-189000.0),
    Point(5700.0,-181800.0),
    Point(6600.0,-181800.0),
    Point(6600.0,-172200.0),
    Point(8400.0,-172200.0),
    Point(8400.0,-144000.0),
    Point(10800.0,-144000.0),
    Point(10800.0,-138300.0),
    Point(12600.0,-138300.0),
    Point(12600.0,-129600.0),
    Point(13500.0,-129600.0),
    Point(13500.0,-85020.0),
    Point(9540.0,-85020.0),
    Point(9540.0,-900.0),
    Point(11820.0,-900.0),
    Point(12900.0,-900.0)]],
"EQU"=>Array[Point[
    Point(75000.0,7200.0),
    Point(75000.0,21600.0),
    Point(75150.0,21600.0),
    Point(75150.0,42600.0),
    Point(75780.0,42600.0),
    Point(76020.0,42600.0),
    Point(76020.0,45000.0),
    Point(76800.0,45000.0),
    Point(76800.0,7200.0)]],
"BOO"=>Array[Point[
    Point(54300.0,28800.0),
    Point(48600.0,28800.0),
    Point(48600.0,54000.0),
    Point(48600.0,102600.0),
    Point(50250.0,102600.0),
    Point(50250.0,110700.0),
    Point(50520.0,110700.0),
    Point(50520.0,174600.0),
    Point(50520.0,199800.0),
    Point(51900.0,199800.0),
    Point(54900.0,199800.0),
    Point(54900.0,190800.0),
    Point(56700.0,190800.0),
    Point(56700.0,185400.0),
    Point(56700.0,144000.0),
    Point(55560.0,144000.0),
    Point(55560.0,118800.0),
    Point(54660.0,118800.0),
    Point(54660.0,93600.0),
    Point(54300.0,93600.0)]],
"CAP"=>Array[Point[
    Point(73920.0,-32400.0),
    Point(72000.0,-32400.0),
    Point(72000.0,-43080.0),
    Point(72000.0,-100800.0),
    Point(73200.0,-100800.0),
    Point(76800.0,-100800.0),
    Point(76800.0,-88200.0),
    Point(78720.0,-88200.0),
    Point(78720.0,-32400.0),
    Point(76800.0,-32400.0),
    Point(76800.0,-54000.0),
    Point(73920.0,-54000.0)]],
"CAE"=>Array[Point[
    Point(15360.0,-144000.0),
    Point(15360.0,-176400.0),
    Point(16200.0,-176400.0),
    Point(16200.0,-163800.0),
    Point(17400.0,-163800.0),
    Point(17400.0,-154800.0),
    Point(18000.0,-154800.0),
    Point(18000.0,-96300.0),
    Point(17400.0,-96300.0),
    Point(16920.0,-96300.0),
    Point(16920.0,-108000.0),
    Point(16500.0,-108000.0),
    Point(16500.0,-133200.0),
    Point(15360.0,-133200.0)]],
"DOR"=>Array[Point[
    Point(13800.0,-190200.0),
    Point(14400.0,-190200.0),
    Point(14400.0,-199800.0),
    Point(15600.0,-199800.0),
    Point(15600.0,-212400.0),
    Point(16500.0,-212400.0),
    Point(16500.0,-239400.0),
    Point(16500.0,-252000.0),
    Point(23700.0,-252000.0),
    Point(23700.0,-230400.0),
    Point(21600.0,-230400.0),
    Point(21600.0,-219600.0),
    Point(19800.0,-219600.0),
    Point(19800.0,-203400.0),
    Point(18000.0,-203400.0),
    Point(18000.0,-194400.0),
    Point(16200.0,-194400.0),
    Point(16200.0,-176400.0),
    Point(15360.0,-176400.0),
    Point(14700.0,-176400.0),
    Point(14700.0,-183600.0),
    Point(13800.0,-183600.0)]],
"LEP"=>Array[Point[
    Point(17400.0,-96300.0),
    Point(18000.0,-96300.0),
    Point(22020.0,-96300.0),
    Point(22020.0,-39600.0),
    Point(21000.0,-39600.0),
    Point(18300.0,-39600.0),
    Point(17700.0,-39600.0),
    Point(17700.0,-48600.0),
    Point(17400.0,-48600.0)]],
"PSA"=>Array[Point[
    Point(82800.0,-88200.0),
    Point(78720.0,-88200.0),
    Point(76800.0,-88200.0),
    Point(76800.0,-100800.0),
    Point(76800.0,-133200.0),
    Point(82800.0,-133200.0)]],
"CRT"=>Array[Point[
    Point(38700.0,-21600.0),
    Point(38700.0,-39600.0),
    Point(38700.0,-68400.0),
    Point(39000.0,-68400.0),
    Point(39000.0,-84600.0),
    Point(42600.0,-84600.0),
    Point(42600.0,-39600.0),
    Point(42600.0,-21600.0),
    Point(41460.0,-21600.0)]],
"OPH"=>Array[Point[
    Point(58560.0,0.0),
    Point(58560.0,14400.0),
    Point(60300.0,14400.0),
    Point(60300.0,46200.0),
    Point(62100.0,46200.0),
    Point(62100.0,51600.0),
    Point(65700.0,51600.0),
    Point(65700.0,43200.0),
    Point(67184.0,43200.0),
    Point(67184.0,22500.0),
    Point(65700.0,22500.0),
    Point(65700.0,16200.0),
    Point(66330.0,16200.0),
    Point(66330.0,10800.0),
    Point(65700.0,10800.0),
    Point(65700.0,0.0),
    Point(64200.0,0.0),
    Point(64200.0,-14400.0),
    Point(64680.0,-14400.0),
    Point(64680.0,-36000.0),
    Point(63600.0,-36000.0),
    Point(63600.0,-37200.0),
    Point(63300.0,-37200.0),
    Point(63300.0,-36000.0),
    Point(61800.0,-36000.0),
    Point(61800.0,-57600.0),
    Point(63360.0,-57600.0),
    Point(63360.0,-108000.0),
    Point(60300.0,-108000.0),
    Point(60300.0,-84300.0),
    Point(58560.0,-84300.0),
    Point(58560.0,-67500.0),
    Point(58950.0,-67500.0),
    Point(58950.0,-63900.0),
    Point(58560.0,-63900.0),
    Point(58560.0,-28800.0),
    Point(57300.0,-28800.0),
    Point(57300.0,-9900.0),
    Point(58560.0,-9900.0)]],
"VIR"=>Array[Point[
    Point(41460.0,0.0),
    Point(41460.0,39600.0),
    Point(42720.0,39600.0),
    Point(42720.0,50400.0),
    Point(46200.0,50400.0),
    Point(46200.0,54000.0),
    Point(48600.0,54000.0),
    Point(48600.0,28800.0),
    Point(54300.0,28800.0),
    Point(54300.0,0.0),
    Point(52800.0,0.0),
    Point(52800.0,-28800.0),
    Point(51300.0,-28800.0),
    Point(51300.0,-79200.0),
    Point(46200.0,-79200.0),
    Point(46200.0,-39600.0),
    Point(42600.0,-39600.0),
    Point(42600.0,-21600.0),
    Point(41460.0,-21600.0)]],
"CAM"=>Array[Point[
    Point(21960.0,201600.0),
    Point(18000.0,201600.0),
    Point(18000.0,189000.0),
    Point(16890.0,189000.0),
    Point(12000.0,189000.0),
    Point(12000.0,198000.0),
    Point(11400.0,198000.0),
    Point(11400.0,205200.0),
    Point(11160.0,205200.0),
    Point(11160.0,244800.0),
    Point(12300.0,244800.0),
    Point(12300.0,277200.0),
    Point(12630.0,277200.0),
    Point(12630.0,288000.0),
    Point(18000.0,288000.0),
    Point(18000.0,306000.0),
    Point(28800.0,306000.0),
    Point(28800.0,311400.0),
    Point(52200.0,311400.0),
    Point(52200.0,288000.0),
    Point(48900.0,288000.0),
    Point(48900.0,277200.0),
    Point(46800.0,277200.0),
    Point(41400.0,277200.0),
    Point(41400.0,288000.0),
    Point(38400.0,288000.0),
    Point(38400.0,295200.0),
    Point(33000.0,295200.0),
    Point(33000.0,264600.0),
    Point(28680.0,264600.0),
    Point(28680.0,216000.0),
    Point(25200.0,216000.0),
    Point(25200.0,223200.0),
    Point(21960.0,223200.0)]],
"COM"=>Array[Point[
    Point(42720.0,50400.0),
    Point(42720.0,104400.0),
    Point(43200.0,104400.0),
    Point(43200.0,122400.0),
    Point(44400.0,122400.0),
    Point(44400.0,115200.0),
    Point(47700.0,115200.0),
    Point(47700.0,102600.0),
    Point(48600.0,102600.0),
    Point(48600.0,54000.0),
    Point(46200.0,54000.0),
    Point(46200.0,50400.0)]],
"VOL"=>Array[Point[
    Point(23700.0,-230400.0),
    Point(23700.0,-252000.0),
    Point(23700.0,-270000.0),
    Point(27600.0,-270000.0),
    Point(32520.0,-270000.0),
    Point(32520.0,-230400.0),
    Point(24600.0,-230400.0)]],
"CEP"=>Array[Point[
    Point(72000.0,214200.0),
    Point(72000.0,221400.0),
    Point(73500.0,221400.0),
    Point(73500.0,241200.0),
    Point(74400.0,241200.0),
    Point(74400.0,270000.0),
    Point(72600.0,270000.0),
    Point(72600.0,288000.0),
    Point(75600.0,288000.0),
    Point(75600.0,309600.0),
    Point(75600.0,310200.0),
    Point(82800.0,310200.0),
    Point(82800.0,316800.0),
    Point(86400.0,316800.0),
    Point(86400.0,226800.0),
    Point(83400.0,226800.0),
    Point(83400.0,212700.0),
    Point(82320.0,212700.0),
    Point(82320.0,202500.0),
    Point(80340.0,202500.0),
    Point(80340.0,198000.0),
    Point(79680.0,198000.0),
    Point(79680.0,189900.0),
    Point(79080.0,189900.0),
    Point(79080.0,197400.0),
    Point(74160.0,197400.0),
    Point(74160.0,219300.0),
    Point(73932.0,219300.0),
    Point(73932.0,214200.0)],

    Point[
    Point(33588.0,316800.0),
    Point(33588.0,311400.0),
    Point(28800.0,311400.0),
    Point(28800.0,306000.0),
    Point(18000.0,306000.0),
    Point(18000.0,288000.0),
    Point(12630.0,288000.0),
    Point(12630.0,277200.0),
    Point(12300.0,277200.0),
    Point(1200.0,277200.0),
    Point(1200.0,237600.0),
    Point(0.0,237600.0),
    Point(0.0,316800.0)
    ]],
"DRA"=>Array[Point[
    Point(33000.0,264600.0),
    Point(33000.0,295200.0),
    Point(38400.0,295200.0),
    Point(38400.0,288000.0),
    Point(41400.0,288000.0),
    Point(41400.0,277200.0),
    Point(46800.0,277200.0),
    Point(46800.0,252000.0),
    Point(50400.0,252000.0),
    Point(50400.0,237600.0),
    Point(56400.0,237600.0),
    Point(56400.0,252000.0),
    Point(59520.0,252000.0),
    Point(59520.0,270000.0),
    Point(63000.0,270000.0),
    Point(63000.0,288000.0),
    Point(64800.0,288000.0),
    Point(64800.0,309600.0),
    Point(75600.0,309600.0),
    Point(75600.0,288000.0),
    Point(72600.0,288000.0),
    Point(72600.0,270000.0),
    Point(74400.0,270000.0),
    Point(74400.0,241200.0),
    Point(73500.0,241200.0),
    Point(73500.0,221400.0),
    Point(72000.0,221400.0),
    Point(72000.0,214200.0),
    Point(71160.0,214200.0),
    Point(71160.0,208800.0),
    Point(69900.0,208800.0),
    Point(69900.0,199800.0),
    Point(68700.0,199800.0),
    Point(68700.0,171000.0),
    Point(65640.0,171000.0),
    Point(65640.0,181800.0),
    Point(61200.0,181800.0),
    Point(61200.0,185400.0),
    Point(56700.0,185400.0),
    Point(56700.0,190800.0),
    Point(54900.0,190800.0),
    Point(54900.0,199800.0),
    Point(51900.0,199800.0),
    Point(51900.0,226800.0),
    Point(48600.0,226800.0),
    Point(48600.0,230400.0),
    Point(43200.0,230400.0),
    Point(43200.0,239400.0),
    Point(40800.0,239400.0),
    Point(40800.0,264600.0)]],
"CMA"=>Array[Point[
    Point(22020.0,-39600.0),
    Point(26520.0,-39600.0),
    Point(26520.0,-118800.0),
    Point(23700.0,-118800.0),
    Point(22020.0,-118800.0),
    Point(22020.0,-96300.0)]],
"OCT"=>Array[Point[
    Point(0.0,-324000.0),
    Point(0.0,-293400.0),
    Point(12600.0,-293400.0),
    Point(12600.0,-307800.0),
    Point(27600.0,-307800.0),
    Point(27600.0,-293400.0),
    Point(64800.0,-293400.0),
    Point(64800.0,-270000.0),
    Point(86400.0,-270000.0),
    Point(86400.0,-324000.0)]],
"AQR"=>Array[Point[
    Point(73920.0,0.0),
    Point(73920.0,7200.0),
    Point(75000.0,7200.0),
    Point(76800.0,7200.0),
    Point(77280.0,7200.0),
    Point(77280.0,9900.0),
    Point(78000.0,9900.0),
    Point(78000.0,6300.0),
    Point(79200.0,6300.0),
    Point(79200.0,7200.0),
    Point(81900.0,7200.0),
    Point(81900.0,0.0),
    Point(81900.0,-14400.0),
    Point(85800.0,-14400.0),
    Point(85800.0,-25200.0),
    Point(85800.0,-88200.0),
    Point(82800.0,-88200.0),
    Point(78720.0,-88200.0),
    Point(78720.0,-32400.0),
    Point(76800.0,-32400.0),
    Point(76800.0,-54000.0),
    Point(73920.0,-54000.0),
    Point(73920.0,-32400.0)]],
"TRI"=>Array[Point[
    Point(6000.0,90000.0),
    Point(6000.0,100800.0),
    Point(5070.0,100800.0),
    Point(5070.0,118800.0),
    Point(5070.0,126000.0),
    Point(7200.0,126000.0),
    Point(7200.0,132300.0),
    Point(9060.0,132300.0),
    Point(9240.0,132300.0),
    Point(9240.0,122400.0),
    Point(9780.0,122400.0),
    Point(9780.0,110400.0),
    Point(8700.0,110400.0),
    Point(8700.0,98100.0),
    Point(6900.0,98100.0),
    Point(6900.0,90000.0)]],
"HYI"=>Array[Point[
    Point(0.0,-293400.0),
    Point(0.0,-270000.0),
    Point(2700.0,-270000.0),
    Point(2700.0,-273600.0),
    Point(4800.0,-273600.0),
    Point(4800.0,-207000.0),
    Point(7800.0,-207000.0),
    Point(7800.0,-239400.0),
    Point(11520.0,-239400.0),
    Point(16500.0,-239400.0),
    Point(16500.0,-252000.0),
    Point(16500.0,-270000.0),
    Point(12600.0,-270000.0),
    Point(12600.0,-293400.0)]],
"PEG"=>Array[Point[
    Point(76800.0,7200.0),
    Point(76800.0,45000.0),
    Point(76020.0,45000.0),
    Point(76020.0,42600.0),
    Point(75780.0,42600.0),
    Point(75780.0,70200.0),
    Point(76500.0,70200.0),
    Point(76500.0,84600.0),
    Point(77100.0,84600.0),
    Point(77100.0,100800.0),
    Point(78240.0,100800.0),
    Point(78240.0,129600.0),
    Point(78750.0,129600.0),
    Point(79200.0,129600.0),
    Point(79200.0,126000.0),
    Point(82140.0,126000.0),
    Point(82140.0,124200.0),
    Point(82320.0,124200.0),
    Point(84600.0,124200.0),
    Point(84600.0,115500.0),
    Point(85500.0,115500.0),
    Point(85500.0,112800.0),
    Point(86400.0,112800.0),
    Point(86400.0,45000.0),
    Point(85800.0,45000.0),
    Point(85800.0,27000.0),
    Point(81900.0,27000.0),
    Point(81900.0,7200.0),
    Point(79200.0,7200.0),
    Point(79200.0,6300.0),
    Point(78000.0,6300.0),
    Point(78000.0,9900.0),
    Point(77280.0,9900.0),
    Point(77280.0,7200.0)],
    Point[
    Point(0.0,112800.0),
    Point(0.0,100800.0),
    Point(240.0,100800.0),
    Point(240.0,79200.0),
    Point(510.0,79200.0),
    Point(510.0,75600.0),
    Point(510.0,45000.0),
    Point(0.0,45000.0),
    Point(0.0,36000.0)]],
"HYA"=>Array[Point[
    Point(29100.0,0.0),
    Point(29100.0,25200.0),
    Point(33300.0,25200.0),
    Point(34500.0,25200.0),
    Point(34500.0,0.0),
    Point(34500.0,-39600.0),
    Point(38700.0,-39600.0),
    Point(38700.0,-68400.0),
    Point(39000.0,-68400.0),
    Point(39000.0,-84600.0),
    Point(42600.0,-84600.0),
    Point(45300.0,-84600.0),
    Point(45300.0,-79200.0),
    Point(46200.0,-79200.0),
    Point(51300.0,-79200.0),
    Point(51300.0,-84600.0),
    Point(53700.0,-84600.0),
    Point(53700.0,-102600.0),
    Point(45300.0,-102600.0),
    Point(45300.0,-118800.0),
    Point(44100.0,-118800.0),
    Point(44100.0,-126000.0),
    Point(39600.0,-126000.0),
    Point(39000.0,-126000.0),
    Point(39000.0,-111000.0),
    Point(38100.0,-111000.0),
    Point(38100.0,-103800.0),
    Point(36900.0,-103800.0),
    Point(36900.0,-91800.0),
    Point(35100.0,-91800.0),
    Point(35100.0,-86400.0),
    Point(33720.0,-86400.0),
    Point(32700.0,-86400.0),
    Point(32700.0,-68400.0),
    Point(30900.0,-68400.0),
    Point(30900.0,-61200.0),
    Point(30120.0,-61200.0),
    Point(30120.0,-39600.0),
    Point(29100.0,-39600.0)]],
"GEM"=>Array[Point[
    Point(22710.0,43200.0),
    Point(22710.0,63000.0),
    Point(22380.0,63000.0),
    Point(22380.0,77400.0),
    Point(21180.0,77400.0),
    Point(21180.0,82200.0),
    Point(21180.0,100800.0),
    Point(23520.0,100800.0),
    Point(23520.0,127800.0),
    Point(26520.0,127800.0),
    Point(27900.0,127800.0),
    Point(27900.0,120600.0),
    Point(28800.0,120600.0),
    Point(28800.0,100800.0),
    Point(28380.0,100800.0),
    Point(28380.0,72000.0),
    Point(28110.0,72000.0),
    Point(28110.0,48600.0),
    Point(27000.0,48600.0),
    Point(27000.0,45000.0),
    Point(25200.0,45000.0),
    Point(25200.0,36000.0),
    Point(24960.0,36000.0),
    Point(24960.0,43200.0)]],
"CHA"=>Array[Point[
    Point(27600.0,-293400.0),
    Point(49200.0,-293400.0),
    Point(49200.0,-270000.0),
    Point(40500.0,-270000.0),
    Point(32520.0,-270000.0),
    Point(27600.0,-270000.0)]],
"CAR"=>Array[Point[
    Point(40500.0,-199800.0),
    Point(39600.0,-199800.0),
    Point(31800.0,-199800.0),
    Point(31800.0,-192600.0),
    Point(30420.0,-192600.0),
    Point(30420.0,-190800.0),
    Point(29400.0,-190800.0),
    Point(29400.0,-177300.0),
    Point(28800.0,-177300.0),
    Point(21600.0,-177300.0),
    Point(21600.0,-185400.0),
    Point(22200.0,-185400.0),
    Point(22200.0,-198000.0),
    Point(23400.0,-198000.0),
    Point(23400.0,-208800.0),
    Point(24600.0,-208800.0),
    Point(24600.0,-230400.0),
    Point(32520.0,-230400.0),
    Point(32520.0,-270000.0),
    Point(40500.0,-270000.0),
    Point(40500.0,-230400.0)]],
"LYN"=>Array[Point[
    Point(26520.0,127800.0),
    Point(26520.0,160200.0),
    Point(24480.0,160200.0),
    Point(24480.0,180000.0),
    Point(23400.0,180000.0),
    Point(23400.0,194400.0),
    Point(21960.0,194400.0),
    Point(21960.0,201600.0),
    Point(21960.0,223200.0),
    Point(25200.0,223200.0),
    Point(25200.0,216000.0),
    Point(28680.0,216000.0),
    Point(30300.0,216000.0),
    Point(30300.0,169200.0),
    Point(33000.0,169200.0),
    Point(33000.0,151200.0),
    Point(34500.0,151200.0),
    Point(34500.0,143100.0),
    Point(33300.0,143100.0),
    Point(33300.0,120600.0),
    Point(28800.0,120600.0),
    Point(27900.0,120600.0),
    Point(27900.0,127800.0)]],
"LYR"=>Array[Point[
    Point(67920.0,91800.0),
    Point(67920.0,93600.0),
    Point(66120.0,93600.0),
    Point(66120.0,108000.0),
    Point(65430.0,108000.0),
    Point(65430.0,171000.0),
    Point(65640.0,171000.0),
    Point(68700.0,171000.0),
    Point(69000.0,171000.0),
    Point(69000.0,156600.0),
    Point(69840.0,156600.0),
    Point(69840.0,131400.0),
    Point(69690.0,131400.0),
    Point(69690.0,108000.0),
    Point(69330.0,108000.0),
    Point(69330.0,99000.0),
    Point(69330.0,91800.0)]],
"CYG"=>Array[Point[
    Point(69330.0,99000.0),
    Point(69330.0,108000.0),
    Point(69690.0,108000.0),
    Point(69690.0,131400.0),
    Point(69840.0,131400.0),
    Point(69840.0,156600.0),
    Point(69000.0,156600.0),
    Point(69000.0,171000.0),
    Point(68700.0,171000.0),
    Point(68700.0,199800.0),
    Point(69900.0,199800.0),
    Point(69900.0,208800.0),
    Point(71160.0,208800.0),
    Point(71160.0,214200.0),
    Point(72000.0,214200.0),
    Point(73932.0,214200.0),
    Point(73932.0,219300.0),
    Point(74160.0,219300.0),
    Point(74160.0,197400.0),
    Point(79080.0,197400.0),
    Point(79080.0,189900.0),
    Point(79080.0,158400.0),
    Point(78870.0,158400.0),
    Point(78870.0,157500.0),
    Point(78750.0,157500.0),
    Point(78750.0,129600.0),
    Point(78240.0,129600.0),
    Point(78240.0,100800.0),
    Point(77100.0,100800.0),
    Point(75300.0,100800.0),
    Point(75300.0,104400.0),
    Point(70800.0,104400.0),
    Point(70800.0,99000.0)]],
"PYX"=>Array[Point[
    Point(30120.0,-61200.0),
    Point(30900.0,-61200.0),
    Point(30900.0,-68400.0),
    Point(32700.0,-68400.0),
    Point(32700.0,-86400.0),
    Point(33720.0,-86400.0),
    Point(33720.0,-126900.0),
    Point(30120.0,-126900.0)]],
"ARA"=>Array[Point[
    Point(59115.0,-216000.0),
    Point(59115.0,-160200.0),
    Point(64200.0,-160200.0),
    Point(64800.0,-160200.0),
    Point(64800.0,-205200.0),
    Point(63000.0,-205200.0),
    Point(63000.0,-239400.0),
    Point(61200.0,-239400.0),
    Point(60600.0,-239400.0),
    Point(60600.0,-234000.0),
    Point(60300.0,-234000.0),
    Point(60300.0,-224700.0),
    Point(59700.0,-224700.0),
    Point(59700.0,-219600.0),
    Point(59115.0,-219600.0)]],
"PSC"=>Array[Point[
    Point(81900.0,0.0),
    Point(81900.0,7200.0),
    Point(81900.0,27000.0),
    Point(85800.0,27000.0),
    Point(85800.0,45000.0),
    Point(86400.0,45000.0),
    Point(86400.0,-25200.0),
    Point(85800.0,-25200.0),
    Point(85800.0,-14400.0),
    Point(81900.0,-14400.0)],
    Point[
    Point(0.0,36000.0),
    Point(0.0,45000.0),
    Point(510.0,45000.0),
    Point(510.0,75600.0),
    Point(3060.0,75600.0),
    Point(3060.0,85500.0),
    Point(2580.0,85500.0),
    Point(2580.0,118800.0),
    Point(5070.0,118800.0),
    Point(5070.0,100800.0),
    Point(6000.0,100800.0),
    Point(6000.0,90000.0),
    Point(6000.0,35700.0),
    Point(7200.0,35700.0),
    Point(7200.0,7200.0),
    Point(1200.0,7200.0),
    Point(1200.0,0.0),
    Point(1200.0,-25200.0),
    Point(0.0,-25200.0)]],
"UMI"=>Array[Point[
    Point(0.0,324000.0),
    Point(0.0,316800.0),
    Point(33588.0,316800.0),
    Point(33588.0,311400.0),
    Point(52200.0,311400.0),
    Point(52200.0,288000.0),
    Point(48900.0,288000.0),
    Point(48900.0,277200.0),
    Point(46800.0,277200.0),
    Point(46800.0,252000.0),
    Point(50400.0,252000.0),
    Point(50400.0,237600.0),
    Point(56400.0,237600.0),
    Point(56400.0,252000.0),
    Point(59520.0,252000.0),
    Point(59520.0,270000.0),
    Point(63000.0,270000.0),
    Point(63000.0,288000.0),
    Point(64800.0,288000.0),
    Point(64800.0,309600.0),
    Point(75600.0,309600.0),
    Point(75600.0,310200.0),
    Point(82800.0,310200.0),
    Point(82800.0,316800.0),
    Point(86400.0,316800.0),
    Point(86400.0,324000.0)
    ]])


function loadData_vKITTI(worlds,variations,model,mode)
    colors = 0; x = 0;
    y = zeros(Float32,224,224,13,1)
    if model in ("RGB_VGG",)
        #RGB
        x = zeros(Float32,224,224,3,1)
        for world in worlds
            for variation in variations
                worldfile = load(joinpath(dataDIR,"vKITTI","world_$(world)_$(variation).jld"))
                if mode == "odd"
                    x = cat(4,x,worldfile["RGB"][:,:,:,1:2:end])
                    y = cat(4,y,worldfile["GT"][:,:,:,1:2:end])
                elseif mode == "even"
                    x = cat(4,x,worldfile["RGB"][:,:,:,2:2:end])
                    y = cat(4,y,worldfile["GT"][:,:,:,2:2:end])
                elseif mode == "firstHalf"
                    x = cat(4,x,worldfile["RGB"][:,:,:,1:div(end,2)])
                    y = cat(4,y,worldfile["GT"][:,:,:,1:div(end,2)])
                elseif mode == "secondHalf"
                    x = cat(4,x,worldfile["RGB"][:,:,:,div(end,2)+1:end])
                    y = cat(4,y,worldfile["GT"][:,:,:,div(end,2)+1:end])
                elseif mode == "full"
                    x = cat(4,x,worldfile["RGB"][:,:,:,:])
                    y = cat(4,y,worldfile["GT"][:,:,:,:])
                end
                colors = worldfile["colors"]
            end
        end
        x = x[:,:,:,2:end]
        y = y[:,:,:,2:end]
    elseif model in ("RGBD_VGG",)
        #RGB + Colorized depth
        x = zeros(Float32,224,224,3,1)
        d = zeros(Float32,224,224,3,1)
        for world in worlds
            for variation in variations
                worldfile = load(joinpath(dataDIR,"vKITTI","world_$(world)_$(variation).jld"))
                if mode == "odd"
                    x = cat(4,x,worldfile["RGB"][:,:,:,1:2:end])
                    d = cat(4,d,worldfile["DepthRGB"][:,:,:,1:2:end])
                    y = cat(4,y,worldfile["GT"][:,:,:,1:2:end])
                elseif mode == "even"
                    x = cat(4,x,worldfile["RGB"][:,:,:,2:2:end])
                    d = cat(4,d,worldfile["DepthRGB"][:,:,:,2:2:end])
                    y = cat(4,y,worldfile["GT"][:,:,:,2:2:end])
                elseif mode == "firstHalf"
                    x = cat(4,x,worldfile["RGB"][:,:,:,1:div(end,2)])
                    d = cat(4,d,worldfile["DepthRGB"][:,:,:,1:div(end,2)])
                    y = cat(4,y,worldfile["GT"][:,:,:,1:div(end,2)])
                elseif mode == "secondHalf"
                    x = cat(4,x,worldfile["RGB"][:,:,:,div(end,2)+1:end])
                    d = cat(4,d,worldfile["DepthRGB"][:,:,:,div(end,2)+1:end])
                    y = cat(4,y,worldfile["GT"][:,:,:,div(end,2)+1:end])
                elseif mode == "full"
                    x = cat(4,x,worldfile["RGB"][:,:,:,:])
                    d = cat(4,d,worldfile["DepthRGB"][:,:,:,:])
                    y = cat(4,y,worldfile["GT"][:,:,:,:])
                end
                colors = worldfile["colors"]
            end
        end
        x = x[:,:,:,2:end]
        d = d[:,:,:,2:end]
        y = y[:,:,:,2:end]
        x = cat(3,x,d)
    elseif model in ("D_VGG",)
        #Colorized depth
        x = zeros(Float32,224,224,3,1)
        for world in worlds
            for variation in variations
                worldfile = load(joinpath(dataDIR,"vKITTI","world_$(world)_$(variation).jld"))
                if mode == "odd"
                    x = cat(4,x,worldfile["DepthRGB"][:,:,:,1:2:end])
                    y = cat(4,y,worldfile["GT"][:,:,:,1:2:end])
                elseif mode == "even"
                    x = cat(4,x,worldfile["DepthRGB"][:,:,:,2:2:end])
                    y = cat(4,y,worldfile["GT"][:,:,:,2:2:end])
                elseif mode == "firstHalf"
                    x = cat(4,x,worldfile["DepthRGB"][:,:,:,1:div(end,2)])
                    y = cat(4,y,worldfile["GT"][:,:,:,1:div(end,2)])
                elseif mode == "secondHalf"
                    x = cat(4,x,worldfile["DepthRGB"][:,:,:,div(end,2)+1:end])
                    y = cat(4,y,worldfile["GT"][:,:,:,div(end,2)+1:end])
                elseif mode == "full"
                    x = cat(4,x,worldfile["DepthRGB"][:,:,:,:])
                    y = cat(4,y,worldfile["GT"][:,:,:,:])
                end
                colors = worldfile["colors"]
            end
        end
        x = x[:,:,:,2:end]
        y = y[:,:,:,2:end]
    end
    return x,y,colors
end

function loadData_DAVIS()
    videoDIR = joinpath(dataDIR,"DAVIS")
    #Train
    xtrain = zeros(Float32,224,224,3,1)
    ytrain = zeros(Float32,224,224,2,1)
    #Test
    xtest = zeros(Float32,224,224,3,1)
    ytest = zeros(Float32,224,224,2,1)
    for video in readdir(videoDIR)
        datafile = load(joinpath(videoDIR,video))

        xtrain = cat(4,xtrain,datafile["x"][:,:,:,1:div(end,2)])
        ytrain = cat(4,ytrain,datafile["y"][:,:,:,1:div(end,2)])

        xtest = cat(4,xtest,datafile["x"][:,:,:,div(end,2)+1:end])
        ytest = cat(4,ytest,datafile["y"][:,:,:,div(end,2)+1:end])
    end
    xtrain = xtrain[:,:,:,2:end]
    ytrain = ytrain[:,:,:,2:end]

    xtest = xtest[:,:,:,2:end]
    ytest = ytest[:,:,:,2:end]

    return xtrain,ytrain,xtest,ytest
end

function loadData_Robot(model,mode)
    ddir = joinpath(dataDIR,"Robot")
    rooms = filter!(r".jld", readdir(ddir))

    x = 0;
    y = zeros(Float32,224,224,5,1)
    if model in ("RGB",)
        #RGB
        x = zeros(Float32,224,224,3,1)
        for room in rooms
            roomfile = load(joinpath(ddir,room))
            seqparts = []
            seqn = size(roomfile["GT"],4)
            if seqn % 4 == 0
                sz = Int(floor(seqn/4))
                for i=1:4
                    push!(seqparts,(i-1)*sz+1:i*sz)
                end
            else
                sz = Int(floor(seqn/4))
                for i=1:4
                    if i!=4
                        push!(seqparts,(i-1)*sz+1:i*sz)
                    else
                        push!(seqparts,(i-1)*sz+1:seqn)
                    end
                end
            end
            if mode == "train"
                x = cat(4,x,roomfile["RGB"][:,:,:,seqparts[1]])
                y = cat(4,y,roomfile["GT"][:,:,:,seqparts[1]])
                x = cat(4,x,roomfile["RGB"][:,:,:,seqparts[2]])
                y = cat(4,y,roomfile["GT"][:,:,:,seqparts[2]])
                x = cat(4,x,roomfile["RGB"][:,:,:,seqparts[4]])
                y = cat(4,y,roomfile["GT"][:,:,:,seqparts[4]])
            elseif mode == "test"
                x = cat(4,x,roomfile["RGB"][:,:,:,seqparts[3]])
                y = cat(4,y,roomfile["GT"][:,:,:,seqparts[3]])
            end
        end
        x = x[:,:,:,2:end]
        y = y[:,:,:,2:end]
    elseif model in ("RGBD",)
        #RGB + Colorized depth
        x = zeros(Float32,224,224,3,1)
        d = zeros(Float32,224,224,3,1)
        for room in rooms
            roomfile = load(joinpath(ddir,room))
            seqparts = []
            seqn = size(roomfile["GT"],4)
            if seqn % 4 == 0
                sz = Int(floor(seqn/4))
                for i=1:4
                    push!(seqparts,(i-1)*sz+1:i*sz)
                end
            else
                sz = Int(floor(seqn/4))
                for i=1:4
                    if i!=4
                        push!(seqparts,(i-1)*sz+1:i*sz)
                    else
                        push!(seqparts,(i-1)*sz+1:seqn)
                    end
                end
            end
            if mode == "train"
                x = cat(4,x,roomfile["RGB"][:,:,:,seqparts[1]])
                d = cat(4,d,roomfile["DepthRGB"][:,:,:,seqparts[1]])
                y = cat(4,y,roomfile["GT"][:,:,:,seqparts[1]])
                x = cat(4,x,roomfile["RGB"][:,:,:,seqparts[2]])
                d = cat(4,d,roomfile["DepthRGB"][:,:,:,seqparts[2]])
                y = cat(4,y,roomfile["GT"][:,:,:,seqparts[2]])
                x = cat(4,x,roomfile["RGB"][:,:,:,seqparts[4]])
                d = cat(4,d,roomfile["DepthRGB"][:,:,:,seqparts[4]])
                y = cat(4,y,roomfile["GT"][:,:,:,seqparts[4]])
            elseif mode == "test"
                x = cat(4,x,roomfile["RGB"][:,:,:,seqparts[3]])
                d = cat(4,d,roomfile["DepthRGB"][:,:,:,seqparts[3]])
                y = cat(4,y,roomfile["GT"][:,:,:,seqparts[3]])
            end
        end
        x = x[:,:,:,2:end]
        d = d[:,:,:,2:end]
        y = y[:,:,:,2:end]
        x = cat(3,x,d)
    elseif model in ("D",)
        #Colorized depth
        x = zeros(Float32,224,224,3,1)
        for room in rooms
            roomfile = load(joinpath(ddir,room))
            seqparts = []
            seqn = size(roomfile["GT"],4)
            if seqn % 4 == 0
                sz = Int(floor(seqn/4))
                for i=1:4
                    push!(seqparts,(i-1)*sz+1:i*sz)
                end
            else
                sz = Int(floor(seqn/4))
                for i=1:4
                    if i!=4
                        push!(seqparts,(i-1)*sz+1:i*sz)
                    else
                        push!(seqparts,(i-1)*sz+1:seqn)
                    end
                end
            end
            if mode == "train"
                x = cat(4,x,roomfile["DepthRGB"][:,:,:,seqparts[1]])
                y = cat(4,y,roomfile["GT"][:,:,:,seqparts[1]])
                x = cat(4,x,roomfile["DepthRGB"][:,:,:,seqparts[2]])
                y = cat(4,y,roomfile["GT"][:,:,:,seqparts[2]])
                x = cat(4,x,roomfile["DepthRGB"][:,:,:,seqparts[4]])
                y = cat(4,y,roomfile["GT"][:,:,:,seqparts[4]])
            elseif mode == "test"
                x = cat(4,x,roomfile["DepthRGB"][:,:,:,seqparts[3]])
                y = cat(4,y,roomfile["GT"][:,:,:,seqparts[3]])
            end
        end
        x = x[:,:,:,2:end]
        y = y[:,:,:,2:end]
    end
    return x,y
end

function savePrediction(ypred,colors,filename)
    im = zeros(Float32,size(ypred,1),size(ypred,2),3)
    for wi=1:size(ypred,1)
        for hi=1:size(ypred,2)
            im[wi,hi,:] = colors[indmax(ypred[wi,hi,:])]
        end
    end
    im = convert(Image{RGB},im)
    im["spatialorder"] = ["x","y"]

    t = splitdir(filename)
    if !isdir(t[1])
        mkpath(t[1])
    end

    Images.save(filename,im)
end


const Q = Quaternions

@enum ProjectionEnum Perspective Orthographic

@field RotationSpeed = 1f0
@field TranslationSpeed = 1f0
@field EyePosition = Vec3f0(3)
@field LookAt = Vec3f0(0)
@field UpVector = Vec3f0(0, 0, 1)
@field Fov = 45f0
@field Near = 0.01f0
@field Far = 100f0
@field ProjectionType = Perspective

abstract type AbstractCamera <: ReactiveComposable end

@field Camera

function default(::Type{Camera}, x::Partial)
    PerspectiveCamera(x.val)
end
function default(::Type{Area}, x::Partial{<: AbstractCamera})
    FRect(default(Area))
end

@reactivecomposed type BasicCamera <: AbstractCamera
    Area
    Projection
    View
    ProjectionView
end

@composed type PerspectiveCamera <: AbstractCamera
    <: BasicCamera
    ProjectionType
    Translation
    Rotation
    RotationSpeed
    TranslationSpeed
    EyePosition
    LookAt
    UpVector
    Fov
    Near
    Far
end

default(::Type{Rotation}, ::Partial{<: AbstractCamera}) = Vec3f0(0, 0, 0)

function add!(cam, ::Type{Translation}, canvas, key, button)
    local last_mousepos::Vec2f0 = Vec2f0(0, 0)
    on(canvas, Mouse.Drag) do drag
        if ispressed(canvas, key) && ispressed(canvas, button)
            if drag == Mouse.down
                #just started pressing, nothing to do yet
                last_mousepos = Vec2f0(canvas[Mouse.Position])
            elseif drag == Mouse.pressed
                 # we need the difference, although I'm wondering if absolute wouldn't be better.
                 # TODO FIND OUT! Definitely would have more precision problems
                mousepos = Vec2f0(canvas[Mouse.Position])
                diff = (last_mousepos - mousepos) * cam[TranslationSpeed]
                last_mousepos = mousepos
                cam[Translation] = Vec3f0(0f0, diff[1], - diff[2])
            end
        end
        return
    end
    on(canvas, Mouse.Scroll) do scroll
        if ispressed(canvas, button)
            cam[Translation] = Vec3f0(scroll[2], 0f0, 0f0)
        end
        return
    end
end
function add!(cam, ::Type{Rotation}, canvas, key, button)
    local last_mousepos::Vec2f0 = Vec2f0(0, 0)
    on(canvas, Mouse.Drag) do drag
        if ispressed(canvas, key) && ispressed(canvas, button)
            if drag == Mouse.down
                last_mousepos = Vec2f0(canvas[Mouse.Position])
            elseif drag == Mouse.pressed
                mousepos = Vec2f0(canvas[Mouse.Position])
                mp = (last_mousepos - mousepos) * cam[RotationSpeed]
                last_mousepos = mousepos
                cam[Rotation] = Vec3f0(mp[1], -mp[2], 0f0)
            end
        end
        return
    end
end
function add!(cam, ::Type{EyePosition}, ::Type{LookAt})
    on(cam, Translation) do translation
        translation == Vec3f0(0) && return

        lookat = cam[LookAt]; eyepos = cam[EyePosition]; up = cam[UpVector]
        projview = cam[ProjectionView]; area = cam[Area]; prjt = cam[ProjectionType]

        dir = eyepos - lookat
        dir_len = norm(dir)
        cam_res = Vec2f0(widths(area))
        zoom, x, y = translation
        zoom *= 0.1f0 * dir_len

        if prjt != Perspective
            x, y = to_worldspace(Vec2f0(x, y), projview, cam_res)
        else
            x, y = (Vec2f0(x, y) ./ cam_res) .* dir_len
        end
        dir_norm = normalize(dir)
        right = normalize(cross(dir_norm, up))
        zoom_trans = dir_norm * zoom

        side_trans = right * (-x) + normalize(up) * y
        newpos = eyepos + side_trans + zoom_trans
        cam[EyePosition] = newpos
        cam[LookAt] = lookat + side_trans
        return
    end
    on(cam, Rotation) do theta_v
        theta_v == Vec3f0(0) && return #nothing to do!
        eyepos_v = cam[EyePosition]; lookat_v = cam[LookAt]; up_v = cam[UpVector]

        dir = normalize(eyepos_v - lookat_v)
        right_v = normalize(cross(up_v, dir))
        up_v  = normalize(cross(dir, right_v))
        rotation = rotate_cam(theta_v, right_v, Vec3f0(0, 0, 1), dir)
        r_eyepos = lookat_v + rotation * (eyepos_v - lookat_v)
        r_up = normalize(rotation * up_v)
        cam[EyePosition] = r_eyepos
        cam[UpVector] = r_up
        return
    end
end

function add!(cam::PerspectiveCamera, ::Type{ProjectionView})
    on(cam, Area, Fov, Near, ProjectionType, LookAt, EyePosition, UpVector) do area, fov, near, projectiontype, lookatv, eyeposition, upvector
        zoom = norm(lookatv - eyeposition)
        # TODO this means you can't set FarClip... SAD!
        far = max(zoom * 5f0, 30f0)
        proj = projection_switch(area, fov, near, far, projectiontype, zoom)
        view = lookat(eyeposition, lookatv, upvector)
        cam[Projection] = proj
        cam[View] = view
        cam[ProjectionView] = proj * view
    end
end

function projection_switch{T <: Real}(
        wh::SimpleRectangle,
        fov::T, near::T, far::T,
        projection::ProjectionEnum, zoom::T
    )
    aspect = T(wh.w / wh.h)
    h = T(tan(fov / 360.0 * pi) * near)
    w = T(h * aspect)
    projection == Perspective && return frustum(-w, w, -h, h, near, far)
    h, w = h * zoom, w * zoom
    orthographicprojection(-w, w, -h, h, near, far)
end

function rotate_cam{T}(
        theta::Vec{3, T},
        cam_right::Vec{3, T}, cam_up::Vec{3, T}, cam_dir::Vec{3, T}
    )
    rotation = one(Q.Quaternion{T})
    # first the rotation around up axis, since the other rotation should be relative to that rotation
    if theta[1] != 0
        rotation *= Q.qrotation(cam_up, theta[1])
    end
    # then right rotation
    if theta[2] != 0
        rotation *= Q.qrotation(cam_right, theta[2])
    end
    # last rotation around camera axis
    if theta[3] != 0
        rotation *= Q.qrotation(cam_dir, theta[3])
    end
    rotation
end

wscale(screenrect, viewrect) = widths(viewrect) ./ widths(screenrect)

function update_cam!(cam::AbstractCamera, area)
    x, y = minimum(area)
    w, h = widths(area) ./ 2f0
    cam[Area] = FRect(area)
    cam[Projection] = orthographicprojection(-w, w, -h, h, -10_000f0, 10_000f0)
    cam[View] = translationmatrix(Vec3f0(-x - w, -y - h, 0))
    return
end

function add!(cam::AbstractCamera, window, ::Type{Pan})
    @needs window: Mouse.Drag
    on(window, Mouse.Drag, Base.RefValue(Vec(0.0, 0.0))) do dragging, startpos
        mp = Vec(window[Mouse.Position])
        if dragging == Mouse.down
            startpos[] = mp
        elseif dragging == Mouse.pressed && ispressed(window, Mouse.middle)
            diff = startpos[] .- mp
            startpos[] = mp
            a = cam[Area]
            st = diff .* wscale(window[Area], a)
            update_cam!(cam, FRect(minimum(a) .+ st, widths(a)))
        end
        return
    end
end
@field Zoom
function add!(cam::AbstractCamera, window, ::Type{Zoom})
    @needs window: Mouse.Scroll, Mouse.Position
    on(window, Mouse.Scroll) do x
        zoom = Float32(x[2])
        if zoom != 0
            a = cam[Area]
            z = 1 + (zoom * 0.10)
            mp = Vec2f0(window[Mouse.Position])
            mp = (mp .* wscale(window[Area], a)) + minimum(a)
            p1, p2 = minimum(a), maximum(a)
            p1, p2 = p1 - mp, p2 - mp # translate to mouse position
            p1, p2 = z * p1, z * p2
            p1, p2 = p1 + mp, p2 + mp
            update_cam!(cam, FRect(p1, p2 - p1))
            z
        end
    end
end

function reset!(cam::AbstractCamera, boundingbox, preserveratio = true)
    w1 = widths(boundingbox)
    if preserveratio
        w2 = widths(cam[Screen][Area])
        ratio = w2 ./ w1
        w1 = if ratio[1] > ratio[2]
            s = w2[1] ./ w2[2]
            Vec2f0(s * w1[2], w1[2])
        else
            s = w2[2] ./ w2[1]
            Vec2f0(w1[1], s * w1[1])
        end
    end
    p = minimum(w1) .* 0.001 # 2mm padding
    update_cam!(cam, FRect(-p, -p, w1 .+ 2p))
    return
end

function selection_rect(
        canvas,
        key = Mouse.left,
        button = Set([Keyboard.left_control, Keyboard.space])
    )
    @needs canvas: (Mouse.Drag, Mouse.Position)
    rect = FRect(0, 0, 0, 0)
    lw = 2f0
    rect_vis = visualize(
        Primitive => rect,
        Pattern => [0.0, lw, 2lw, 3lw, 4lw],
        Thickness => lw,
        Color => RGBA(0.7f0, 0.7f0, 0.7f0, 0.9f0)
    )
    dragged_rect = on(canvas, Mouse.Drag) do drag
        if ispressed(canvas, key) && ispressed(canvas, button)
            if drag == Mouse.down
                rect_vis[Visible] = true # start displaying
                rect_vis[Primitive] = IRect(canvas[Mouse.Position], 0, 0)
            elseif drag == Mouse.pressed
                min = minimum(rect_vis[Primitive])
                wh = canvas[Mouse.Position] - min
                rect_vis[Primitive] = FRect(min, wh)
            end
        else
            # always hide if not the right key is pressed
            rect_vis[Visible] = false # hide
        end
        return
    end
    push!(canvas, rect_vis)
    rect_vis
end

lerp{T}(a::T, b::T, val::AbstractFloat) = (a .+ (val * (b .- a)))

function add_restriction!(cam::AbstractCamera, window, rarea::SimpleRectangle, minwidths::Vec)
    area_ref = Base.RefValue(cam[Area])
    restrict_action = paused_action(1.0) do t
        o = lerp(origin(area_ref[]), origin(cam[Area]), t)
        wh = lerp(widths(area_ref[]), widths(cam[Area]), t)
        update_cam!(cam, FRect(o, wh))
    end
    on(window, Mouse.Drag) do drag
        if drag == Mouse.up && !isplaying(restrict_action)
            area = cam[Area]
            o = origin(area)
            maxi = maximum(area)
            newo = max.(o, origin(rarea))
            newmax = min.(maxi, maximum(rarea))
            maxi = maxi - newmax
            newo = newo - maxi
            newwh = newmax - newo
            scale = 1f0
            for (w1, w2) in zip(minwidths, newwh)
                stmp = w1 > w2 ? w1 / w2 : 1f0
                scale = max(scale, stmp)
            end
            newwh = newwh * scale
            area_ref[] = FRect(newo, newwh)
            if area_ref[] != cam[Area]
                play!(restrict_action)
            end
        end
        return
    end
    restrict_action
end


@info "RoME is adding Flux related functionality."

# Required packages
using .Flux

include("models/Pose2OdoNN_01.jl") # until a better way is found to deserialize

# make sure the lambdas get defined early
buildPose2OdoNN_01_FromElements();

# FluxModelsPose2Pose2

# the factor definitions
export FluxModelsPose2Pose2, PackedFluxModelsPose2Pose2
# some utilities
export setShuffleAll!, setNaiveFracAll!

using Random, Statistics
using DistributedFactorGraphs, TransformUtils

import Base: convert
import IncrementalInference: getSample, calcZDim

struct FluxModelsPose2Pose2{P,D<:AbstractArray,M<:SamplableBelief} <: AbstractRelativeRoots
  allPredModels::Vector{P}
  joyVelData::D
  naiveModel::M
  naiveFrac::Ref{Float64}
  # TODO type stability likely wants parameter Pose2Pose2{<:SamplableBelief}
  Zij::Pose2Pose2{<:SamplableBelief}
  DT::Ref{Float64}
  shuffle::Ref{Bool}
end

function calcVelocityInterPose2!( nfb::FluxModelsPose2Pose2,
                                  iPts::AbstractMatrix{<:Real},
                                  jPts::AbstractMatrix{<:Real})
  #
  # DXY[1:2,i] .= TransformUtils.R(iPts[3,i])'*DXY[1:2,i]
  nfb.joyVelData[1,3:4] .= jPts[1:2]
  nfb.joyVelData[1,3:4] .-= iPts[1:2]
  nfb.joyVelData[1,3:4] ./= nfb.DT[] # convert to velocity
  nfb.joyVelData[1,3:4] .= TransformUtils.R(iPts[3])'*nfb.joyVelData[1,3:4]
  # just set zero if something is wrong
  if isnan(nfb.joyVelData[1,3]) || isinf(abs(nfb.joyVelData[1,3])) || isnan(nfb.joyVelData[1,4]) || isinf(abs(nfb.joyVelData[1,4]))
    nfb.joyVelData[1,3:4] .= 0.0
  end
  for i in 2:size(nfb.joyVelData,1)
    nfb.joyVelData[i,3:4] .= nfb.joyVelData[1,3:4]
  end
end

# function calcVelocityInterPose2!( nfb::FluxModelsPose2Pose2,
#                                   iPts::AbstractMatrix{<:Real},
#                                   jPts::AbstractMatrix{<:Real}  )
#   #
#   @assert size(jPts,2) == size(iPts,2) "sampleFluxModelsPose2Pose2 can currently only evaluate equal population size variables"

#   # calculate an average velocity component
#   if nfb.DT[] == 0
#     # DXY = (@view jPts[1:2,:]) - (@view jPts[1:2,:])
#     # rotate delta position from world to local iX frame
#     for i in 1:size(iPts,2)
#       calcVelocityInterPose2!(nfb, iPts, jPts, i)
#     end
#   else
#     nfb.joyVelData[:,3:4] .= 0.0
#   end
#   nothing
# end

# sampleFluxModelsPose2Pose2
function IIF.getSample(cfo::CalcFactor{<:FluxModelsPose2Pose2}, N::Int=1)
  #
  nfb = cfo.factor
  fmd = cfo.metadata
  Xi = fmd.fullvariables[1] # X
  Xj = fmd.fullvariables[2] # A / B

  # get the naive samples
  # model samples (all for theta at this time)
  smplsNaive = rand(nfb.naiveModel, N)

  # calculate naive model and Predictive fraction of samples, respectively
  selectSource = rand(Categorical([nfb.naiveFrac[]; 1-nfb.naiveFrac[]]), N)

  # number of predictors to choose from, and choose random subset
  numModels = length(nfb.allPredModels)
  allPreds = 1:numModels |> collect # 1:Npreds |> collect
  # TODO -- compensate when there aren't enough prediction models
  if numModels < N
    repeat(allPreds, ceil(Int, (N-numModels)/N) + 1)
    allPreds = allPreds[1:N]
  end
  # samples for the order in which to use models, dont shuffle if N models
  # can suppress shuffle for NN training purposes
  1 < numModels && nfb.shuffle[] ? Random.shuffle!(allPreds) : nothing

  # cache the time difference estimate
  nfb.DT[] = (getTimestamp(Xj) - getTimestamp(Xi)).value * 1e-3

  return (smplsNaive, selectSource, allPreds)
end

# Convenience function to help call the right constuctor
FluxModelsPose2Pose2( allModels::Vector,
                      jvd::AbstractMatrix,
                      naiveModel::SamplableBelief,
                      naiveFrac::Float64=0.5,
                      DT::Float64=0.0,
                      shuffle::Bool=true ) = FluxModelsPose2Pose2(
                                        allModels,
                                        jvd,
                                        naiveModel,
                                        Ref(naiveFrac),
                                        Pose2Pose2(MvNormal(zeros(3),diagm(ones(3)))), # this dummy distribution does not get used
                                        Ref(DT),
                                        Ref(shuffle) )
#
# struct FluxModelsPose2Pose2{P,D<:AbstractArray,M<:SamplableBelief} <: AbstractRelativeRoots
#   allPredModels::Vector{P}
#   joyVelData::D
#   naiveModel::M
#   naiveFrac::Ref{Float64}
#   # TODO type stability likely wants parameter Pose2Pose2{<:SamplableBelief}
#   Zij::Pose2Pose2{<:SamplableBelief}
#   DT::Ref{Float64}
#   shuffle::Ref{Bool}
# end

function (cfo::CalcFactor{<:FluxModelsPose2Pose2})(meas1,meas2,meas3,Xi,Xj)
# function (nfb::FluxModelsPose2Pose2)(
            # res::AbstractArray{<:Real},
            # userdata::FactorMetadata,
            # idx::Int,
            # meas::Tuple{AbstractArray{<:Real},AbstractArray{<:Int},AbstractArray{<:Int}},
            # Xi::AbstractArray{<:Real,2},
            # Xj::AbstractArray{<:Real,2}  )
  #
  userdata = cfo.metadata
  nfb = cfo.factor
  fmd = cfo.metadata
  meas = (meas1, meas2, meas3)

  ## MAYBE JUST MOVE THIS INTO getSample and be done
  # if, use prediction sample
  if meas[2] == 2 # meas2 == 2
    # get live velocity estimate for each sample (nfb.joyVelData[:,3:4])
    calcVelocityInterPose2!(nfb, Xi, Xj)
    # predict odom for this sample from a specific prediction model
    meas[1][1:2] = nfb.allPredModels[meas[3]](nfb.joyVelData)
  end

  # calculate the error for that measurement sample as Pose2Pose2
  #TODO
  cfZij = CalcFactor( nfb.Zij, nothing, 0, 0, (), [])
  res = cfZij((meas[1],), Xi, Xj)

end

"""
    $SIGNATURES

Helper function to assemble the data required for each model.
"""
function assembleNNFactorData(dfg::AbstractDFG, fctsym::Symbol, idx::Int)
  #
  f1 = getFactor(dfg, fctsym)
  nfb = getFactorType(f1)
  varsyms = getVariableOrder(f1)
  Xi = getVariable(dfg, varsyms[1]) |> getBelief |> getPoints
  Xj = getVariable(dfg, varsyms[2]) |> getBelief |> getPoints

  # add the specific velocity information
  calcVelocityInterPose2!(nfb, Xi, Xj, idx)
  nfb.joyVelData
end

## packing converters

struct PackedFluxModelsPose2Pose2 <: IncrementalInference.PackedInferenceType
  joyVelData::Vector{Vector{Float64}}
  naiveModel::String
  naiveFrac::Float64
  modelWeightsAll::Vector{Dict{Symbol,Vector}}
end
    # W1::Matrix{Float64}
    # b1::Vector{Float64}
    # W2::Matrix{Float64}
    # b2::Vector{Float64}
    # W3::Matrix{Float64}
    # b3::Vector{Float64}
  # allPredModels::Vector{P}
  # joyVelData::D
  # naiveModel::M
  # naiveFrac::Ref{Float64}
  # Zij::Pose2Pose2
  # specialSampler::Function # special keyword field name used to invoke 'specialSampler' logic
  # DT::Ref{Float64}

function convert(::Type{FluxModelsPose2Pose2}, d::PackedFluxModelsPose2Pose2)
  MDS = []
  for mdw in d.modelWeightsAll
    # @show keys(mdw) |> collect
    mW1 = mdw[:W1]
    @cast W1c[row,col] := mW1[row][col]
    W1 = W1c |> Matrix{Float32}
    @assert W1[1,1] - mdw[:W1][1][1] |> abs < 1e-6 "Something about matrix vector order in FluxModelsPose2Pose2 converter broke."
    @assert W1[1,2] - mdw[:W1][1][2] |> abs < 1e-6 "Something about matrix vector order in FluxModelsPose2Pose2 converter broke."
    @assert W1[2,1] - mdw[:W1][2][1] |> abs < 1e-6 "Something about matrix vector order in FluxModelsPose2Pose2 converter broke."
    b1 = mdw[:b1] |> Vector{Float32}
    mW2 = (mdw[:W2])
    @cast W2c[row,col] := mW2[row][col]
    W2 = W2c |> Matrix{Float32}
    b2 = mdw[:b2] |> Vector{Float32}
    mW3 = (mdw[:W3])
    @cast W3c[row,col] := mW3[row][col]
    W3 = W3c |> Matrix{Float32}
    b3 = mdw[:b3] |> Vector{Float32}
    md = buildPose2OdoNN_01_FromElements(W1,b1,W2,b2,W3,b3)
    push!(MDS, md)
  end
  @cast joyVel[row,col] := d.joyVelData[row][col]
  joyVelM = joyVel |> Matrix{Float64}
  FluxModelsPose2Pose2(MDS, joyVelM, extractdistribution(d.naiveModel), d.naiveFrac )
end

# need to overwrite for specialSampler
# TODO rather consolidate with getDimension(::Pose2Pose2)
function calcZDim(usrfnc::FluxModelsPose2Pose2, Xi::Vector{<:DFGVariable})
  return 3
end

function convert(::Type{PackedFluxModelsPose2Pose2}, d::FluxModelsPose2Pose2)
  MDS = []
  for md in d.allPredModels
    mddict =Dict{Symbol, AbstractArray}()
    @cast W1c[row][col] := md[1].W1[row,col]
    @cast W2c[row][col] := md[5].W[row,col]
    @cast W3c[row][col] := md[6].W[row,col]
    mddict[:W1] = Vector{Float32}[vec(W1c[i]) for i in 1:length(W1c)]
    mddict[:b1] = md[1].b1
    mddict[:W2] = Vector{Float32}[vec(W2c[i]) for i in 1:length(W2c)]
    # mddict[:W2] = Vector{Vector{Float32}}(Vector{Float32}(W2c))
    mddict[:b2] = md[5].b
    mddict[:W3] = Vector{Float32}[vec(W3c[i]) for i in 1:length(W3c)]
    # mddict[:W3] = Vector{Vector{Float32}}(Vector{Float32}(W3c))
    mddict[:b3] = md[6].b
    push!(MDS, mddict)
  end
  joyVel = Vector{Float64}[d.joyVelData[i,:] for i in 1:size(d.joyVelData,1)]
  PackedFluxModelsPose2Pose2(joyVel, string(d.naiveModel), d.naiveFrac[], MDS)
end

## Utilities

function setShuffleAll!(dfg::AbstractDFG, shuf::Bool)
  fs = lsf(dfg, FluxModelsPose2Pose2)
  (x->getFactorType(dfg, x).shuffle[] = shuf).(fs)
  nothing
end

function setNaiveFracAll!(dfg::AbstractDFG, frac::Real)
  fs = lsf(dfg, FluxModelsPose2Pose2)
  (x->getFactorType(dfg, x).naiveFrac[] = frac).(fs)
  nothing
end

# function FMP2P2Other()
#   calcVelocityInterPose2!(nfb, getVal(Xi), getVal(Xj) )
#   # sample predictive fraction
#
#   # and predict
#       # A = [rand(4) for i in 1:25]
#   smplsPredAll = nfb.predictFnc(nfb.joyVelData)
#   # add rotation dimension if not done by the prediction function
#   if size(smplsPredAll,2) == 2
#     smplsPredAll = hcat(smplsPredAll, zeros(size(smplsPredAll,1)))
#   end
#
#
#   Nn = sum(selectSource .== 2)
#   # calculate desired number of predicted values
#   Np = N - Nn
#   # randomly select particles for prediction (with possible duplicates forwhen Np > size(iPts,2))
#   Npp = Np < numModels ? Np : numModels
#   Nnn = N - Npp
#   selPreds = @view selectPredModel[1:Npp] # TODO better in-place
#   smpls_p = smplsPredAll[selPreds,:] # sample per row??
#   smpls_p[:,3] .= smplsNaive[3,Nnn+1:N] # use naive delta theta at this time
#   # naive fraction
#   smpls_n = @view smplsNaive[:, 1:Nnn] # sample per column
#   # join and shuffle predicted odo values
#   shfSmpl = shuffle!(1:N |> collect)
#   smpls = hcat(smpls_n, smpls_p')[:,shfSmpl]
# end

#


@testset "4.4.1.2 (d csc)^m (a+b cot)^n" begin
    (a, b, n, x, ) = @variables a b n x

    #= ::Package:: =#

    #= ::Title:: =#
    #=Integrands*of*the*form*(d*csc(e+f*x))^m*(a+b*cot(e+f*x))^n=#

    #= ::Section::Closed:: =#
    #=Integrands*of*the*form*(d*csc(e+f*x))^m*(a+b*cot(e+f*x))^n*with*a^2+b^2=0=#

    #= ::Subsection::Closed:: =#
    #=Integrands*of*the*form*csc(e+f*x)^m*(a+a*im*cot(e+f*x))^n=#

    #= ::Subsubsection:: =#
    #=n>0=#

    #= ::Subsubsection::Closed:: =#
    #=n<0=#

    @test_int [sin(x)^4/(im + cot(x)), x, 4, -((5*im*x)/16) + 1/(32*(im - cot(x))^2) - im/(8*(im - cot(x))) - im/(24*(im + cot(x))^3) - 3/(32*(im + cot(x))^2) + (3*im)/(16*(im + cot(x)))]
    @test_int [sin(x)^3/(im + cot(x)), x, 3, (4/5)*im*cos(x) - (4/15)*im*cos(x)^3 + (im*sin(x)^3)/(5*(im + cot(x)))]
    @test_int [sin(x)^2/(im + cot(x)), x, 4, -((3*im*x)/8) - im/(8*(im - cot(x))) - 1/(8*(im + cot(x))^2) + im/(4*(im + cot(x)))]
    @test_int [sin(x)^1/(im + cot(x)), x, 2, (2/3)*im*cos(x) + (im*sin(x))/(3*(im + cot(x)))]
    @test_int [csc(x)^1/(im + cot(x)), x, 1, (im*csc(x))/(im + cot(x))]
    @test_int [csc(x)^2/(im + cot(x)), x, 2, (-im)*x + log(sin(x))]
    @test_int [csc(x)^3/(im + cot(x)), x, 2, im*atanh(cos(x)) - csc(x)]
    @test_int [csc(x)^4/(im + cot(x)), x, 2, im*cot(x) - cot(x)^2/2]
    @test_int [csc(x)^5/(im + cot(x)), x, 3, (1/2)*im*atanh(cos(x)) + (1/2)*im*cot(x)*csc(x) - csc(x)^3/3]
    @test_int [csc(x)^6/(im + cot(x)), x, 3, im*cot(x) - cot(x)^2/2 + (1/3)*im*cot(x)^3 - cot(x)^4/4]
    @test_int [csc(x)^7/(im + cot(x)), x, 4, (3/8)*im*atanh(cos(x)) + (3/8)*im*cot(x)*csc(x) + (1/4)*im*cot(x)*csc(x)^3 - csc(x)^5/5]

    #= ::Section::Closed:: =#
    #=Integrands*of*the*form*(d*csc(e+f*x))^m*(a+b*cot(e+f*x))^n=#

    #= ::Subsection::Closed:: =#
    #=Integrands*of*the*form*csc(e+f*x)^m*(a+b*cot(e+f*x))^n=#

    #= ::Subsubsection:: =#
    #=n>0=#

    #= ::Subsubsection::Closed:: =#
    #=n<0=#

    @test_int [csc(x)^6/(a + b*cot(x)), x, 3, (a*(a^2 + 2*b^2)*cot(x))/b^4 - ((a^2 + 2*b^2)*cot(x)^2)/(2*b^3) + (a*cot(x)^3)/(3*b^2) - cot(x)^4/(4*b) - ((a^2 + b^2)^2*log(a + b*cot(x)))/b^5]
    @test_int [csc(x)^4/(a + b*cot(x)), x, 3, (a*cot(x))/b^2 - cot(x)^2/(2*b) - ((a^2 + b^2)*log(a + b*cot(x)))/b^3]
    @test_int [csc(x)^2/(a + b*cot(x)), x, 2, -(log(a + b*cot(x))/b)]
    @test_int [sin(x)^2/(a + b*cot(x)), x, 7, (a*(a^2 + 3*b^2)*x)/(2*(a^2 + b^2)^2) - (b^3*log(b*cos(x) + a*sin(x)))/(a^2 + b^2)^2 - ((b + a*cot(x))*sin(x)^2)/(2*(a^2 + b^2))]
    @test_int [sin(x)^4/(a + b*cot(x)), x, 8, (a*(3*a^4 + 10*a^2*b^2 + 15*b^4)*x)/(8*(a^2 + b^2)^3) - (b^5*log(b*cos(x) + a*sin(x)))/(a^2 + b^2)^3 - ((4*b^3 + a*(3*a^2 + 7*b^2)*cot(x))*sin(x)^2)/(8*(a^2 + b^2)^2) - ((b + a*cot(x))*sin(x)^4)/(4*(a^2 + b^2))]

    @test_int [csc(x)^5/(a + b*cot(x)), x, 9, (a*atanh(cos(x)))/(2*b^2) + (a*(a^2 + b^2)*atanh(cos(x)))/b^4 + ((a^2 + b^2)^(3/2)*atanh(((b - a*cot(x))*sin(x))/sqrt(a^2 + b^2)))/b^4 - ((a^2 + b^2)*csc(x))/b^3 + (a*cot(x)*csc(x))/(2*b^2) - csc(x)^3/(3*b)]
    @test_int [csc(x)^3/(a + b*cot(x)), x, 5, (a*atanh(cos(x)))/b^2 + (sqrt(a^2 + b^2)*atanh(((b - a*cot(x))*sin(x))/sqrt(a^2 + b^2)))/b^2 - csc(x)/b]
    @test_int [csc(x)^1/(a + b*cot(x)), x, 2, -(atanh(((-b + a*cot(x))*sin(x))/sqrt(a^2 + b^2))/sqrt(a^2 + b^2))]
    @test_int [sin(x)^1/(a + b*cot(x)), x, 5, (b^2*atanh(((b - a*cot(x))*sin(x))/sqrt(a^2 + b^2)))/(a^2 + b^2)^(3/2) - (a*cos(x))/(a^2 + b^2) - (b*sin(x))/(a^2 + b^2)]
    @test_int [sin(x)^3/(a + b*cot(x)), x, 9, (b^4*atanh(((b - a*cot(x))*sin(x))/sqrt(a^2 + b^2)))/(a^2 + b^2)^(5/2) - (a*b^2*cos(x))/(a^2 + b^2)^2 - (a*cos(x))/(a^2 + b^2) + (a*cos(x)^3)/(3*(a^2 + b^2)) - (b^3*sin(x))/(a^2 + b^2)^2 - (b*sin(x)^3)/(3*(a^2 + b^2))]

    @test_int [csc(x)^2/(a + b*cot(x))^2, x, 2, 1/(b*(a + b*cot(x)))]

    #= ::Subsection:: =#
    #=Integrands*of*the*form*csc(e+f*x)^m*(a+b*cot(e+f*x))^(n/2)=#

    #= ::Subsection:: =#
    #=Integrands*of*the*form*csc(e+f*x)^(m/2)*(a+b*cot(e+f*x))^n=#

    #= ::Subsection:: =#
    #=Integrands*of*the*form*csc(e+f*x)^(m/2)*(a+b*cot(e+f*x))^(n/2)=#

    #= ::Subsection::Closed:: =#
    #=Integrands*of*the*form*(d*csc(e+f*x))^m*(a+b*cot(e+f*x))^n*with*n*symbolic=#
    #==#

    @test_int [csc(x)^2*(a + b*cot(x))^n, x, 2, -((a + b*cot(x))^(1 + n)/(b*(1 + n)))]

    #= ::Subsection:: =#
    #=Integrands*of*the*form*sin(e+f*x)^(m/2)*(a+b*cot(e+f*x))^n=#

    #= ::Subsection:: =#
    #=Integrands*of*the*form*sin(e+f*x)^(m/2)*(a+b*cot(e+f*x))^(n/2)=#
    end

test/4 Trig functions/4.4 Cotangent/4.4.1.2 (d csc)^m (a+b cot)^n.jl


# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/Materials.jl/blob/master/LICENSE
#
# Low-level definitions for one_elem_disp_chaboche.jl.

mutable struct Continuum3D <: FieldProblem
    material_model :: Symbol
end

Continuum3D() = Continuum3D(:PerfectPlastic)
FEMBase.get_unknown_field_name(::Continuum3D) = "displacement"

function FEMBase.assemble_elements!(problem::Problem{Continuum3D},
                                    assembly::Assembly,
                                    elements::Vector{Element{Hex8}},
                                    time::Float64)

    for element in elements
        for ip in get_integration_points(element)
            material = ip("material", time)
            preprocess_increment!(material, element, ip, time)
        end
    end
    bi = BasisInfo(Hex8)

    dim = 3
    nnodes = 8
    ndofs = dim*nnodes

    BL = zeros(6, ndofs)
    Km = zeros(ndofs, ndofs)
    f_int = zeros(ndofs)
    f_ext = zeros(ndofs)
    D = zeros(6, 6)
    S = zeros(6)

    dtime = 0.05
    # super dirty hack
    # data = first(elements).fields["displacement"].data
    # if length(data) > 1
    #     time0 = data[end-1].first
    #     dtime = time - time0
    # end

    for element in elements

        u = element("displacement", time)
        fill!(Km, 0.0)
        fill!(f_int, 0.0)
        fill!(f_ext, 0.0)

        for ip in get_integration_points(element)
            J, detJ, N, dN = element_info!(bi, element, ip, time)
            material = ip("material", time)
            w = ip.weight*detJ

            # Kinematic matrix, linear part

            fill!(BL, 0.0)
            for i=1:nnodes
                BL[1, 3*(i-1)+1] = dN[1,i]
                BL[2, 3*(i-1)+2] = dN[2,i]
                BL[3, 3*(i-1)+3] = dN[3,i]
                BL[4, 3*(i-1)+1] = dN[2,i]
                BL[4, 3*(i-1)+2] = dN[1,i]
                BL[5, 3*(i-1)+2] = dN[3,i]
                BL[5, 3*(i-1)+3] = dN[2,i]
                BL[6, 3*(i-1)+1] = dN[3,i]
                BL[6, 3*(i-1)+3] = dN[1,i]
            end

            # Calculate stress response
            integrate_material!(material)
            D = material.jacobian
            S = material.stress + material.dstress
            #@info("material matrix", D)

            # Material stiffness matrix
            Km += w*BL'*D*BL

            # Internal force vector
            f_int += w*BL'*S

            # External force vector
            for i=1:dim
                haskey(element, "displacement load $i") || continue
                b = element("displacement load $i", ip, time)
                f_ext[i:dim:end] += w*B*vec(N)
            end

        end

        # add contributions to K, Kg, f
        gdofs = get_gdofs(problem, element)
        add!(assembly.K, gdofs, gdofs, Km)
        add!(assembly.f, gdofs, f_ext - f_int)

    end

    return nothing
end

function FEMBase.assemble_elements!(problem::Problem{Continuum3D},
                                    assembly::Assembly,
                                    elements::Vector{Element{Quad4}},
                                    time::Float64)

    nnodes = 4
    ndofs = 3
    f = zeros(nnodes*ndofs)
    bi = BasisInfo(Quad4)

    for element in elements

        fill!(f, 0.0)

        for ip in get_integration_points(element)

            J, detJ, N, dN = element_info!(bi, element, ip, time)
            w = ip.weight*detJ

            if haskey(element, "surface pressure")
                J = element(ip, time, Val{:Jacobian})'
                n = cross(J[:,1], J[:,2])
                n /= norm(n)
                # sign convention, positive pressure is towards surface
                p = element("surface pressure", ip, time)
                f += w*p*vec(n*N)
            end
        end

        gdofs = get_gdofs(problem, element)
        add!(assembly.f, gdofs, f)

    end

    return nothing

end


##########
##########
# Create series of networks to run through simulations
##########
##########
# AUTHOR: Cole B. Brookson
# DATE OF CREATION: 2021-12-04
##########
##########

# set up =======================================================================

# set packages
using DelimitedFiles

include("../01_species_list_creation.jl")

# create different combinations of networks ====================================

# the different values that have different options are:
#   richness shape
#   biomass shape

# set all positional values
organism_id_pos = 1
organism_type_pos = 2
body_size_pos = 3
lower_prey_limit_pos = 4
upper_prey_limit_pos = 5
habitat_midpoint_pos = 6
habitat_lower_limit_pos = 7
habitat_upper_limit_pos = 8
nutritional_value_pos = 9
biomass_pos = 10
network_id_pos = 11
alive_pos = 12

# set parameters
n = 50
richness_shape = "top-heavy"
species_list, network = network_draw(n, richness_shape)
# draw species list
species_list = define_species_list(n, richness_shape)

# test create_interaction_matrix()
interaction_matrix = fill(0, (size(species_list, 1), size(species_list, 1)))
    for i in 1:size(interaction_matrix)[1] #rows
        for j in 1:size(interaction_matrix)[2] #cols
            interaction_matrix[i,j] = determine_interaction(j, i, species_list)
        end
    end
    return interaction_matrix
interaction_matrix = create_interaction_matrix(species_list)
sums = minimum(sum(interaction_matrix, dims = 2) +
            (sum(interaction_matrix, dims = 1)'))
if sums == 0
    # go through while loop to re-draw until connected
    connect_acomplish = false
    connected_counter = 0
    while connect_acomplish == false
        connected_counter += 1
        # draw species list
        species_list = define_species_list(n, richness_shape)
        interaction_matrix = create_interaction_matrix(species_list)

        # turn into graph
        sums = minimum(sum(interaction_matrix, dims = 2) +
            (sum(interaction_matrix, dims = 1)'))
        if sums == 0
            continue
        end

        if sums > 0
            connect_acomplish = true
        end
    end
end
return species_list, interaction_matrix


# A list of functions

# Overloading an input function so anything can be put into it and it just
# returns the input
function f_input(x::Any, y::Any, c::Any)
    x
end

# For output Nodes, if input is a number and anything else, return the number
function f_output(x::Float64, y::Any, c::Any)
    x
end

# For output Nodes, if input is an array and anything else, return the mean of
# the array
function f_output(x::Array{Float64}, y::Any, c::Any)
    mean(x)
end

# Scale all values to the proper input range
function scaled(x::Float64)
    if isnan(x)
        return 0.0
    end
    min(max(x, -1.0), 1.0)
end

# Scale all values to the proper input range
function scaled(x::Array{Float64})
    x[isnan.(x)] = 0.0
    min.(max.(x, -1.0), 1.0)
end

# Not super sure what's going on here
function func2f(f::Function)
    findfirst(CGP.Config.functions .== f) / length(CGP.Config.functions)
end

# Function to find function index
function f2ind(list::Tuple, i::Float64)
    l = length(list)
    min(max(Int64(ceil(i*l)), 1), l)
end

# Function to find function index
function f2ind(list::Tuple, i::Array{Float64})
    l = length(list)
    min.(max.(Int64.(ceil.(i.*l)), 1), l)
end

# Function to find function index
function f2ind(list::Array, i::Float64)
    l = length(list)
    min(max(Int64(ceil(i*l)), 1), l)
end

# Function to find function index
function f2ind(list::Array, i::Array{Float64})
    l = length(list)
    min.(max.(Int64.(ceil.(i.*l)), 1), l)
end

# Converts list of functions to indices (?)
function index_in(list::Array, index::Float64)
    list[f2ind(list, abs(index))]
end

# Converts list of functions to indices (?)
function index_in(list::Array, index::Array{Float64})
    index_in(list, mean(abs.(index)))
end

# Converts list of functions to indices (?)
function index_in(list::Tuple, index::Float64)
    list[f2ind(list, abs(index))]
end

# Converts list of functions to indices (?)
function index_in(list::Tuple, index::Array{Float64})
    index_in(list, mean(abs.(index)))
end

#TODO: n-dimensional square indexing

# A segmentation function ?
function segmentation(x::Array{Float64}, p::Float64, f::Function)
    if ndims(x) == 2
        segments = f(x, p)
        return segments.image_indexmap / maximum(segments.segment_labels)
    else
        return x
    end
end

# Gets a range?
function range_in(list::Array{Float64}, xi::Float64, yi::Float64)
    # TODO: multi-dimensional
    bounds = [min(xi, yi), max(xi, yi)]
    bounds = f2ind(list, abs.(bounds))
    if bounds[1] == bounds[2]
        return 0.0
    end
    list[bounds[1]:bounds[2]]
end

# Gets a range?
function range_in(list::Array{Float64}, xi::Array{Float64}, yi::Float64)
    range_in(list, mean(xi), yi)
end

# Scaling indices
function scaled_indmax(x::Array{Float64})
    scaled(collect((ind2sub(x, indmax(x)) .- 1) ./ (size(x) .- 1)))
end

# Scaling indices
scaled_indmax(x::Array{Float64}, y::Array{Float64}) = scaled_indmax(x)

# I have no clue what's happening here
function com(x::Array{Float64})
    # TODO: speed up
    com = Tuple(zeros(ndims(x)))
    sx = sum(x.+1)
    if sx > 0
        for i in eachindex(x)
            com = com .+ ((x[i]+1) .* (ind2sub(x, i) .- 1 .- ((size(x) .- 1)./2)))
        end
        com = com ./ sx
    end
    scaled(collect((com .+ (size(x) .- 1) ./ 2) ./ (size(x) .- 1)))
end

# Gets the minimum sized array
function minsize(x::Array{Float64}, y::Array{Float64})
    dims = min(ndims(x), ndims(y))
    shape = [min(size(x, i), size(y, i)) for i in 1:dims]
    (x[[1:i for i in shape]...], y[[1:i for i in shape]...])
end

# Fills a smaller array with 1s
function fillsize(x::Array{Float64}, y::Array{Float64}, c::Float64)
    if ndims(x) == ndims(y)
        return paddedviews(c, x, y)
    elseif ndims(x) > ndims(y)
        mindim = ndims(y)+1
        return paddedviews(c, x, repeat(y, inner=tuple(
            ones(Int64, ndims(y))..., size(x)[mindim:end]...)))
    else
        mindim = ndims(x)+1
        return paddedviews(c, repeat(x, inner=tuple(
            ones(Int64, ndims(x))..., size(y)[mindim:end]...)), y)
    end
end

# Checks whether two arrays are equally sized
function eqsize(x::Array{Float64}, y::Array{Float64}, c::Float64)
    minsize(x, y)
end

# Generates funtions from strings
function sgen(name::String, s1::String, s2::String, s3::String, s4::String)
    eval(parse(string(name,
                      "(x::Float64, y::Float64, c::Float64=0.0)=",
                      s1, ";", name,
                      "(x::Float64, y::Array{Float64}, c::Float64=0.0)=",
                      s2, ";", name,
                      "(x::Array{Float64}, y::Float64, c::Float64=0.0)=",
                      s3, ";", name,
                      "(x::Array{Float64}, y::Array{Float64}, c::Float64=0.0)=",
                      s4)))
end

# Loads all the functions from a dictionary of functions
function load_functions(funs::Dict)
    newfuns = []
    for k in keys(funs)
        if isdefined(Config, parse(k))
            debug("Loading functions: $k is already defined, skipping")
        else
            if length(funs[k])==1
                sgen(k, funs[k][1], funs[k][1], funs[k][1], funs[k][1])
            elseif length(funs[k])==2
                sgen(k, funs[k][1], funs[k][1], funs[k][2], funs[k][2])
            else
                sgen(k, funs[k][1], funs[k][2], funs[k][3], funs[k][4])
            end
            append!(newfuns, [k])
        end
    end
    [eval(parse(k)) for k in newfuns]
end