def testPolyline(self): p = czml.Polyline() p.color = {'rgba': [0, 255, 127, 55]} self.assertEqual(p.data(), {'color': {'rgba': [0, 255, 127, 55]}, 'show': False}) p.outlineColor = {'rgbaf': [0.0, 0.255, 0.127, 0.55]} self.assertEqual(p.data(), {'color': {'rgba': [0, 255, 127, 55]}, 'outlineColor': {'rgbaf': [0.0, 0.255, 0.127, 0.55]}, 'show': False}) p.width = 10 p.outlineWidth = 2 p.show = True self.assertEqual(p.data(), {'color': {'rgba': [0, 255, 127, 55]}, 'width': 10, 'outlineColor': {'rgbaf': [0.0, 0.255, 0.127, 0.55]}, 'outlineWidth': 2, 'show': True}) p2 = czml.Polyline() p2.loads(p.dumps()) self.assertEqual(p.data(), p2.data())
packet = czml.CZMLPacket( id=rail['segment'].lower().replace(' ', ""), status="t") #id=str(random.randint(0, 99999)), name=rail['segment']) packet._name = rail['segment'] packet._description = DummyObject(rail['segment']) props = list(packet._properties) props.append("status") packet._properties = props #print(packet._properties) packet.status = DummyObject(rail['new_track']) #packet._description = "t"#str(rail['new_track']) positions = czml.Positions(cartographicDegrees=positions) pl = czml.Polyline(positions=positions) if rail['new_track'].lower() == "new": color = {'rgba': [255, 0, 0, 255]} else: color = {'rgba': [0, 0, 255, 255]} pl.material = czml.Material(solidColor=czml.SolidColor(color=color)) pl.width = 5 pl.clampToGround = True # https://icons-for-free.com/iconfiles/png/512/express+harry+hogwarts+potter+train+icon-1320183591487406864.png packet.polyline = pl doc.packets.append(packet) # time packet filename = "../wwwroot/test/rails.czml"
def testCZMLPacket(self): p = czml.CZMLPacket(id='abc') self.assertEqual(p.dumps(), '{"id": "abc"}') bb = czml.Billboard() bb.image = 'http://localhost/img.png' bb.scale = 0.7 bb.show = True p.billboard = bb self.assertEqual(p.data(), {'billboard': {'image': 'http://localhost/img.png', 'scale': 0.7, 'show': True}, 'id': 'abc'}) p2 = czml.CZMLPacket(id='abc') p2.loads(p.dumps()) self.assertEqual(p.data(), p2.data()) pos = czml.Position() coords = [7.0, 0.0, 1.0, 2.0, 6.0, 3.0, 4.0, 5.0] pos.cartesian = coords p.position = pos l = czml.Label() l.text = 'test label' l.show = False p.label = l self.assertEqual(p.data(), {'billboard': {'image': 'http://localhost/img.png', 'scale': 0.7, 'show': True}, 'id': 'abc', 'label': {'show': False, 'text': 'test label'}, 'position': {'cartesian': [7.0, 0.0, 1.0, 2.0, 6.0, 3.0, 4.0, 5.0]}, }) p2.loads(p.dumps()) self.assertEqual(p.data(), p2.data()) p3 = czml.CZMLPacket(id='cde') p3.point = {'color': {'rgba': [0, 255, 127, 55]}, 'show': True} self.assertEqual(p3.data(), {'id': 'cde', 'point': {'color': {'rgba': [0, 255, 127, 55]}, 'show': True}}) p32 = czml.CZMLPacket(id='abc') p32.loads(p3.dumps()) self.assertEqual(p3.data(), p32.data()) p4 = czml.CZMLPacket(id='defg') pl = czml.Polyline() pl.color = {'rgba': [0, 255, 127, 55]} pl.width = 10 pl.outlineWidth = 2 pl.show = True v = czml.VertexPositions() v.cartographicDegrees = [0.0, 0.0, .0, 1.0, 1.0, 1.0] p4.vertexPositions = v p4.polyline = pl self.assertEqual(p4.data(), {'polyline': {'color': {'rgba': [0, 255, 127, 55]}, 'width': 10, 'outlineWidth': 2, 'show': True}, 'id': 'defg', 'vertexPositions': {'cartographicDegrees': [0.0, 0.0, 0.0, 1.0, 1.0, 1.0]} }) p42 = czml.CZMLPacket(id='abc') p42.loads(p4.dumps()) self.assertEqual(p4.data(), p42.data()) p5 = czml.CZMLPacket(id='efgh') p5.vertexPositions = v poly = czml.Polygon(color={'rgba': [0, 255, 127, 55]}) p5.polygon = poly self.assertEqual(p5.data(), {'polygon': {'material': {'solidColor': {'color': {'rgba': [0, 255, 127, 55]}}}}, 'id': 'efgh', 'vertexPositions': {'cartographicDegrees': [0.0, 0.0, 0.0, 1.0, 1.0, 1.0]}}) p52 = czml.CZMLPacket(id='abc') p52.loads(p5.dumps()) self.assertEqual(p5.data(), p52.data()) return p
def testPolyline(self): # Create a new polyline sc = czml.SolidColor(color={'rgba': [0, 255, 127, 55]}) m1 = czml.Material(solidColor=sc) c1 = geometry.LineString([(-162, 41, 0), (-151, 43, 0), (-140, 45, 0)]) v1 = czml.Positions(cartographicDegrees=c1) p1 = czml.Polyline(show=True, width=5, followSurface=False, material=m1, positions=v1) self.assertEqual( p1.data(), { 'show': True, 'width': 5, 'followSurface': False, 'material': { 'solidColor': { 'color': { 'rgba': [0, 255, 127, 55] } }, }, 'positions': { 'cartographicDegrees': [-162, 41, 0, -151, 43, 0, -140, 45, 0] }, }) # Create a new polyline pg = czml.PolylineGlow(color={'rgba': [0, 255, 127, 55]}, glowPower=0.25) m2 = czml.Material(polylineGlow=pg) c2 = geometry.LineString([(1.6, 5.3, 10), (2.4, 4.2, 20), (3.8, 3.1, 30)]) v2 = czml.Positions(cartographicRadians=c2) p2 = czml.Polyline(show=False, width=7, followSurface=True, material=m2, positions=v2) self.assertEqual( p2.data(), { 'show': False, 'width': 7, 'followSurface': True, 'material': { 'polylineGlow': { 'color': { 'rgba': [0, 255, 127, 55] }, 'glowPower': 0.25 }, }, 'positions': { 'cartographicRadians': [1.6, 5.3, 10, 2.4, 4.2, 20, 3.8, 3.1, 30] }, }) # Create a polyline from an existing polyline p3 = czml.Polyline() p3.loads(p2.dumps()) self.assertEqual(p3.data(), p2.data()) # Modify an existing polyline po = czml.PolylineOutline(color={'rgba': [0, 255, 127, 55]}, outlineColor={'rgba': [0, 55, 127, 255]}, outlineWidth=4) m3 = czml.Material(polylineOutline=po) c3 = geometry.LineString([(1000, 7500, 90), (2000, 6500, 50), (3000, 5500, 20)]) v3 = czml.Positions(cartesian=c3) p3.material = m3 p3.positions = v3 self.assertEqual( p3.data(), { 'show': False, 'width': 7, 'followSurface': True, 'material': { 'polylineOutline': { 'color': { 'rgba': [0, 255, 127, 55] }, 'outlineColor': { 'rgba': [0, 55, 127, 255] }, 'outlineWidth': 4 }, }, 'positions': { 'cartesian': [1000, 7500, 90, 2000, 6500, 50, 3000, 5500, 20] }, }) # Add a polyline to a CZML packet packet = czml.CZMLPacket(id='abc') packet.polyline = p3 self.assertEqual( packet.data(), { 'id': 'abc', 'polyline': { 'show': False, 'width': 7, 'followSurface': True, 'material': { 'polylineOutline': { 'color': { 'rgba': [0, 255, 127, 55] }, 'outlineColor': { 'rgba': [0, 55, 127, 255] }, 'outlineWidth': 4 }, }, 'positions': { 'cartesian': [1000, 7500, 90, 2000, 6500, 50, 3000, 5500, 20] }, }, })
def add_weighted_network(self, demographics, network, gradient_spec, opacity_func): """Adds a weighted network visualization layer to a CZML output. This method emits a CZML animation that provides a visual representation of a weighted network between nodes. Returns: Number of network segments added Args: demographics (Demographics): Demographics object for nodes. network (array): array of objects:: { from: <from-node-id>, to: <to-node-id>, weight: <float-weight> } gradient_spec (str): gradient spec for a gradient with which to color the network lines. opacity_func (function): function(weight, norm_weight) that returns the desired opacity in range [0,1]. """ # First pass - collect min/max rate min_weight = network[0]["weight"] max_weight = network[0]["weight"] for segment in network: weight = segment["weight"] min_weight = weight if weight < min_weight else min_weight max_weight = weight if weight > max_weight else max_weight weight_range = max_weight - min_weight # Second pass - precalculate norm_weight and opacity for segment in network: weight = segment["weight"] norm_weight = (weight - min_weight) / weight_range segment["norm_weight"] = norm_weight segment["opacity"] = opacity_func(weight, norm_weight) # Sort network by opacity, lowest opacity first # network.sort(key=lambda seg: seg["opacity"]) def sort_func(a, b): diff = a["opacity"] - b["opacity"] if diff < 0: return -1 elif diff == 0: return 0 else: return 1 if sys.version_info.major == 3: # python 3: use a key function network = sorted(network, key=functools.cmp_to_key(sort_func)) else: # python 2: use a cmp function network = sorted(network, cmp=sort_func) gradient = Gradient(gradient_spec) count = 0 for segment in network: from_node = demographics[segment["from"]] to_node = demographics[segment["to"]] color = gradient.sample(segment["norm_weight"]).to_rgba_array() color[3] = int(segment["opacity"] * 255) # id = repr(segment["from"]) + "-" + repr(segment["to"]) id = count packet = czml.CZMLPacket(id=id) positions = { "cartographicDegrees": [ from_node["NodeAttributes"]["Longitude"], from_node["NodeAttributes"]["Latitude"], 0, to_node["NodeAttributes"]["Longitude"], to_node["NodeAttributes"]["Latitude"], 0 ] } line = czml.Polyline( show=True, positions=positions, width=1, material={"solidColor": { "color": { "rgba": color } }}) packet.polyline = line self.doc.packets.append(packet) count += 1 if self._verbose: print("CZMLWriter.add_network: %d network segments added." % count) return count
def add_simplified_vector_migrations(self, vector_migrations, demographics, migration_duration_timesteps, arrow_color, arrow_thickness_pixels): """Adds vector cohort migration animations to a CZML output. This function, given vector migrations in a particular format and a demographics file, adds "comet" animations for migration events. This function expects the following fields in vector_migrations: * Time (int): the timestep of the beginning of the migration event * FromNodeID (int): the node ID from which the migration emanates * ToNodeID (int): the node ID to which the migration completes Returns: Number of vector cohort migrations in animation layer Args: vector_migrations (CSVReport): The ReportVectorMigrations.csv report. demographics (Demographics): The Demographics object describing the nodes. migration_duration_timesteps (int): The duration of the migration animations in timesteps. arrow_color (string): A CSS #rrggbb color for the migration arrow. arrow_thickness_pixels (float): Thickness in pixels of comet tail. """ czml.Material._properties = \ ('grid', 'image', 'stripe', 'solidColor', 'polylineGlow', 'polylineOutline', 'polylineArrow') # This is a little ugly - I run-time extend Material with a # PolylineArrow property, since the one in module czml lacks that. class PolylineArrow(czml._CZMLBaseObject): """Colors the line with a color and an arrow.""" _color = None _properties = ('color', ) czml.Material._polylineArrow = None czml.Material.polylineArrow = czml.class_property( PolylineArrow, 'polylineArrow', doc="""Colors the line with a color and an arrow.""") count = 0 for row in vector_migrations: timestep = int(row["Time"]) from_node_id = row["FromNodeID"] to_node_id = row["ToNodeID"] if not (from_node_id in demographics and to_node_id in demographics): continue from_node = demographics[from_node_id] to_node = demographics[to_node_id] dur_seconds = migration_duration_timesteps * 60 * 60 * 24 availability = self._timestep_to_iso(timestep) + "/" +\ self._timestep_to_iso(timestep + migration_duration_timesteps) id_txt = "%s-%s@%d_%d" % (from_node_id, to_node_id, timestep, count) ac = Color.from_html_hash(arrow_color) packet = czml.CZMLPacket(id=id_txt, availability=availability) polyline = czml.Polyline( followSurface=False, positions={ "cartographicDegrees": [ from_node["NodeAttributes"]["Longitude"], from_node["NodeAttributes"]["Latitude"], 0, # altitude to_node["NodeAttributes"]["Longitude"], to_node["NodeAttributes"]["Latitude"], 0 # altitude ] }, material={ "polylineArrow": { "color": { "epoch": self._timestep_to_iso(timestep), "rgba": [ 0, ac.r, ac.g, ac.b, 255, dur_seconds + 60 * 60 * 12, ac.r, ac.g, ac.b, 0 ] } } }, width=arrow_thickness_pixels) packet.polyline = polyline self.doc.packets.append(packet) count += 1 if self._verbose: print("CZMLWriter.add_vector_migrations: %d migrations added." %\ count) return count
def czml(self): doc = czml.CZML(); iso = self.date.isoformat() # Generate time-specific lists for various objects central_polyline_degrees = [] north_polyline_degrees = [] south_polyline_degrees = [] ellipse_position = [] ellipse_semiMajorAxis = [] ellipse_semiMinorAxis = [] ellipse_rotation = [] for t in range(len(self.time)): time = iso + "T" + self.time[t] + ":00Z" # Define polyline waypoints only where data exist if self.position['north'][t] != None: north_polyline_degrees += [self.position['north'][t][0], self.position['north'][t][1], 0.0] if self.position['central'][t] != None: central_polyline_degrees += [self.position['central'][t][0], self.position['central'][t][1], 0.0] if self.position['south'][t] != None: south_polyline_degrees += [self.position['south'][t][0], self.position['south'][t][1], 0.0] # Define ellipse positions and attributes for every time in the interval, using limits where necessary use_limit = min(int(math.floor(t/(len(self.time)/2))),1) if self.position['north'][t] == None: north = self.limits['north'][use_limit] else: north = self.position['north'][t] if self.position['central'][t] == None: central = self.limits['central'][use_limit] else: central = self.position['central'][t] if self.position['south'][t] == None: south = self.limits['south'][use_limit] else: south = self.position['south'][t] # Approximate ellipse semiMajorAxis from vincenty distance between limit polylines north2 = (north[1], north[0]) south2 = (south[1], south[0]) semi_major_axis = vincenty(north2, south2).meters / 2 # Approximate elipse semiMinorAxis from sun altitude (probably way wrong!) ellipse_axis_ratio = self.sun_altitude[t] / 90 semi_minor_axis = semi_major_axis * ellipse_axis_ratio # Approximate ellipse rotation using basic spheroid (TODO: replace with WGS-84) # Calculate bearing in both directions and average them nlat = north[1]/180 * math.pi; nlon = north[0]/180 * math.pi; clat = central[1]/180 * math.pi; clon = central[0]/180 * math.pi; slat = south[1]/180 * math.pi; slon = south[0]/180 * math.pi; y = math.sin(slon-nlon) * math.cos(slat); x = math.cos(nlat) * math.sin(slat) - math.sin(nlat) * math.cos(slat) * math.cos(slon-nlon); initial_bearing = math.atan2(y, x) if (initial_bearing < 0): initial_bearing += math.pi * 2 y = math.sin(nlon-slon) * math.cos(nlat); x = math.cos(slat) * math.sin(nlat) - math.sin(slat) * math.cos(nlat) * math.cos(nlon-slon); final_bearing = math.atan2(y, x) - math.pi if (final_bearing < 0): final_bearing += math.pi * 2 rotation = -1 * ((initial_bearing + final_bearing) / 2 - (math.pi / 2)) ellipse_position += [time, central[0], central[1], 0.0] ellipse_semiMajorAxis += [time, round(semi_major_axis, 3)] ellipse_semiMinorAxis += [time, round(semi_minor_axis, 3)] ellipse_rotation += [time, round(rotation, 3)] # Generate document packet with clock start_time = iso + "T" + self.time[0] + ":00Z" end_time = iso + "T" + self.time[-1] + ":00Z" packet = czml.CZMLPacket(id='document',version='1.0') c = czml.Clock() c.multiplier = 300 c.range = "LOOP_STOP" c.step = "SYSTEM_CLOCK_MULTIPLIER" c.currentTime = start_time c.interval = start_time + "/" + end_time packet.clock = c doc.packets.append(packet) # Generate a polyline packet for the north and south polylines, connected and filled limit_polyline_degrees = list(north_polyline_degrees) point = len(south_polyline_degrees)/3 while (point > 0): offset = (point-1) * 3 limit_polyline_degrees += [ south_polyline_degrees[offset], south_polyline_degrees[offset+1], south_polyline_degrees[offset+2] ] point -= 1 packet_id = iso + '_bounds_polygon' packet = czml.CZMLPacket(id=packet_id) boc = czml.Color(rgba=(232, 72, 68, 255)) bsc = czml.SolidColor(color=czml.Color(rgba=(0, 0, 0, 66))) bmat = czml.Material(solidColor=bsc) bdeg = limit_polyline_degrees bpos = czml.Positions(cartographicDegrees=bdeg) bpg = czml.Polygon(show=True, height=0, outline=True, outlineColor=boc, outlineWidth=2, material=bmat, positions=bpos) packet.polygon = bpg doc.packets.append(packet) # Generate central polyline packet packet_id = iso + '_central_polyline' packet = czml.CZMLPacket(id=packet_id) csc = czml.SolidColor(color=czml.Color(rgba=(241, 226, 57, 255))) cmat = czml.Material(solidColor=csc) cpos = czml.Positions(cartographicDegrees=central_polyline_degrees) cpl = czml.Polyline(show=True, width=4, followSurface=True, material=cmat, positions=cpos) packet.polyline = cpl doc.packets.append(packet) # Generate ellipse shadow packet packet_id = iso + '_shadow_ellipse' packet = czml.CZMLPacket(id=packet_id) esc = czml.SolidColor(color=czml.Color(rgba=(0, 0, 0, 160))) emat = czml.Material(solidColor=esc) xmaj = czml.Number(ellipse_semiMajorAxis) xmin = czml.Number(ellipse_semiMinorAxis) rot = czml.Number(ellipse_rotation) ell = czml.Ellipse(show=True, fill=True, granularity=0.002, material=emat, semiMajorAxis=xmaj, semiMinorAxis=xmin, rotation=rot) packet.ellipse = ell packet.position = czml.Position(cartographicDegrees=ellipse_position) doc.packets.append(packet) return list(doc.data())