@@ -45,6 +45,11 @@ class _SolarSystem(_UniqueObject): | |||
return self._data["security"] | |||
@property | |||
def coords(self): | |||
"""The solar system's coordinates, as a 3-tuple of floats (x, y, z).""" | |||
return tuple(self._data["coords"]) | |||
@property | |||
def faction(self): | |||
"""The solar system's faction, as a _Faction object, or None.""" | |||
if "faction" in self._data: | |||
@@ -66,6 +71,11 @@ class _SolarSystem(_UniqueObject): | |||
"""Whether the solar system is in nullsec.""" | |||
return self.security >= 0.45 | |||
@property | |||
def is_whspace(self): | |||
"""Whether the solar system is in wormhole space.""" | |||
return self.region.is_whspace | |||
class _Constellation(_UniqueObject): | |||
"""Represents a constellation.""" | |||
@@ -87,6 +97,11 @@ class _Constellation(_UniqueObject): | |||
return self._universe.faction(self._data["faction"]) | |||
return self.region.faction | |||
@property | |||
def is_whspace(self): | |||
"""Whether the constellation is in wormhole space.""" | |||
return self.region.is_whspace | |||
class _Region(_UniqueObject): | |||
"""Represents a region.""" | |||
@@ -103,6 +118,11 @@ class _Region(_UniqueObject): | |||
return self._universe.faction(self._data["faction"]) | |||
return None | |||
@property | |||
def is_whspace(self): | |||
"""Whether the region is in wormhole space.""" | |||
return self._id >= 11000000 | |||
class _Faction(_UniqueObject): | |||
"""Represents a faction.""" | |||
@@ -173,7 +193,8 @@ class _DummySolarSystem(_SolarSystem): | |||
"name": "Unknown", | |||
"constellation": -1, | |||
"region": -1, | |||
"security": 0.0 | |||
"security": 0.0, | |||
"coords": (0, 0, 0) | |||
}) | |||
@@ -288,6 +309,12 @@ class Universe: | |||
return _DummySolarSystem(self) | |||
return _SolarSystem(self, sid, self._systems[sid]) | |||
def systems(self): | |||
"""Return an iterator over all _SolarSystems.""" | |||
self._load() | |||
for sid in self._systems: | |||
yield self.system(sid) | |||
def constellation(self, cid): | |||
"""Return a _Constellation with the given ID. | |||
@@ -1,8 +1,9 @@ | |||
# -*- coding: utf-8 -*- | |||
from flask import g, json | |||
from flask_mako import render_template | |||
from ._provided import blueprint | |||
from ._provided import app, blueprint | |||
def home(): | |||
"""Render and return the main map page.""" | |||
@@ -16,3 +17,22 @@ def navitem(): | |||
def map(): | |||
"""Render and return the main map page.""" | |||
return home() | |||
@blueprint.rroute("/map/data.json") | |||
def mapdata(): | |||
"""Render and return the map data as a JSON object.""" | |||
payload = { | |||
"galaxy": { | |||
system.id: { | |||
"name": system.name, | |||
"coords": system.coords, | |||
"security": system.security | |||
} | |||
for system in g.eve.universe.systems() if not system.is_whspace | |||
} | |||
} | |||
resp = app.response_class(response=json.dumps(payload), status=200, | |||
mimetype="application/json") | |||
resp.cache_control.private = True | |||
resp.cache_control.max_age = 24 * 60 * 60 | |||
return resp |
@@ -150,7 +150,9 @@ def _build_galaxy_skeleton(ids, names): | |||
assert isinstance(sid, int) | |||
d[sid] = { | |||
"name": names[sid], | |||
"constellation": -1, "region": -1, "security": 0.0 | |||
"constellation": -1, | |||
"region": -1, | |||
"security": 0.0 | |||
} | |||
print("done.") | |||
@@ -160,15 +162,18 @@ def _load_assoc_for_system(galaxy, system, rid, cid): | |||
data = _load_yaml(system / "solarsystem.staticdata") | |||
sid = data["solarSystemID"] | |||
sec = data["security"] | |||
coords = data["center"] | |||
assert isinstance(sid, int) | |||
assert isinstance(sec, float) | |||
assert sid >= 0 | |||
assert sec >= -1.0 and sec <= 1.0 | |||
assert len(coords) == 3 and all(isinstance(val, float) for val in coords) | |||
galaxy["systems"][sid]["constellation"] = cid | |||
galaxy["systems"][sid]["region"] = rid | |||
galaxy["systems"][sid]["security"] = sec | |||
galaxy["systems"][sid]["coords"] = coords | |||
if "factionID" in data: | |||
facid = data["factionID"] | |||
@@ -0,0 +1,3 @@ | |||
#map .system { | |||
fill: currentColor; | |||
} |
@@ -0,0 +1,47 @@ | |||
$(function() { | |||
$("#map").html("<p>Loading map data...</p>"); | |||
$.getJSON( "map/data.json", function(data) { | |||
$("#map").empty(); | |||
var svg = d3.select("#map").append("svg") | |||
.attr("width", 1000) | |||
.attr("height", 1000); // TODO: dynamic | |||
var xmin = Infinity, xmax = -Infinity, | |||
ymin = Infinity, ymax = -Infinity; | |||
for (var sid in data["galaxy"]) { | |||
var system = data["galaxy"][sid]; | |||
var x = system["coords"][0]; | |||
var y = system["coords"][2]; | |||
if (x < xmin) xmin = x; | |||
if (x > xmax) xmax = x; | |||
if (y < ymin) ymin = y; | |||
if (y > ymax) ymax = y; | |||
} | |||
var width = xmax - xmin; | |||
var height = ymax - ymin; | |||
var scale = 1000; | |||
svg.attr("viewBox", "0 0 " + scale + " " + scale); | |||
svg.selectAll("circle") | |||
.data(Object.values(data["galaxy"])) | |||
.enter() | |||
.append("circle") | |||
.attr("cx", (d) => { | |||
var x = d["coords"][0]; | |||
return (x - xmin) / width * scale; | |||
}) | |||
.attr("cy", (d) => { | |||
var y = d["coords"][2]; | |||
return (y - ymin) / height * scale; | |||
}) | |||
.attr("r", 1) | |||
.attr("class", (d) => { | |||
var sec = d["security"]; | |||
var klass = sec < 0.05 ? "null" : | |||
Number(sec).toFixed(1).replace(".", "_"); | |||
return "system sec-" + klass; | |||
}); | |||
}); | |||
}); |
@@ -2,6 +2,16 @@ | |||
<%block name="title"> | |||
${self.support.maketitle("Map")} | |||
</%block> | |||
<%block name="extracss"> | |||
${self.support.makecss("map.css")} | |||
</%block> | |||
<%block name="extrajs"> | |||
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.4.1/d3.min.js" integrity="sha256-4mL8TQfOJSbg0f42dQw5cKLl2ngQXUSXqfQnvK11M44=" crossorigin="anonymous"></script> | |||
${self.support.makejs("map.js")} | |||
</%block> | |||
<h2>Map</h2> | |||
## TODO | |||
<p>No map available yet!</p> | |||
<div id="map"> | |||
<noscript> | |||
<p>JavaScript is required to display the galaxy map.</p> | |||
</noscript> | |||
</div> |