diff --git a/backend/promis/backend_api/serializer.py b/backend/promis/backend_api/serializer.py index 310ea3e..ad2fc12 100644 --- a/backend/promis/backend_api/serializer.py +++ b/backend/promis/backend_api/serializer.py @@ -16,6 +16,7 @@ from backend_api import helpers import parsers import unix_time +import math class LookupById: @@ -78,19 +79,8 @@ def get_geo_line(self, obj): # Just in case for the future #return obj.geo_line.wkb.hex() - poly = self.context['request'].query_params.get('polygon') - - # Convert polygon to GEOS object as intersection doesn't auto-convert - if poly: - try: - poly = GEOSGeometry(poly, srid = 4326) - except ValueError: - raise NotFound("Invalid WKT for polygon selection") - - geo_line = obj.geo_line if not poly else obj.geo_line.intersection(poly) - # TODO: study whether pre-building the list or JSON would speed up things - return parsers.wkb(geo_line.wkb) # <- Generator + return parsers.wkb(obj.geo_line.wkb) # <- Generator def get_timelapse(self, obj): # TODO: change to time_start in model for consistency @@ -260,3 +250,67 @@ def update(self, instance, validated_data): password = validated_data.pop('password') instance.set_password(password) return super().update(instance, validated_data) + +# TODO: REFACTOR, alpha code below +class DataSerializer(serializers.ModelSerializer): + session = SwaggerHyperlinkedRelatedField(many = False, view_name = 'session-detail', read_only = True) + channel = SwaggerHyperlinkedRelatedField(many = False, view_name = 'channel-detail', read_only = True) + parameter = SwaggerHyperlinkedRelatedField(many = False, view_name = 'parameter-detail', read_only = True) + selection = serializers.SerializerMethodField() + + + class Meta(LookupById): + # TODO: add 'url' here, currently it's broken, see #196 + fields = ('id', 'session', 'parameter', 'channel', 'sampling_frequency', 'min_frequency', 'max_frequency', 'selection') + model = models.Measurement + + def get_selection(self, obj): + def gen_selection(): + # Extracting request information + poly = self.context['request'].query_params.get('polygon') + time_begin = self.context['request'].query_params.get('time_begin') + time_end = self.context['request'].query_params.get('time_end') + + # Agreeing on time boundaries + session_start = unix_time.datetime_to_utc(obj.session.time_begin) + session_end = unix_time.datetime_to_utc(obj.session.time_end) + time_begin = int(time_begin) if time_begin else session_start + time_end = int(time_end) if time_end else session_end + + # Convert polygon to GEOS object as intersection doesn't auto-convert + if poly: + try: + poly = GEOSGeometry(poly, srid = 4326) + + # FIXME: Django's GEOS implementation + # doesn't support 4D linestrings + # TODO: patch upstream, it's horrible! + + # Intersection of search polygon and the orbit + for sect in obj.session.geo_line.intersection(poly): + # NOTE: taking the first and last point within the selection + sect_start = math.ceil(sect[0][2]) + session_start + sect_end = math.floor(sect[-1][2]) + session_start + + # TODO: if you ever catch this assert, call me + assert sect_end >= sect_start + + print(sect_start, sect_end) + print(time_begin, time_end) + + # If the data not within time selection, skip completely + if sect_end < time_begin or sect_start > time_end: + continue + + yield { 'start': max(time_begin, sect_start), 'end': min(time_end, sect_end) } + + + except ValueError: + raise NotFound("Invalid WKT for polygon selection") + # Otherwise take the whole session, just make sure to trim the time values + else: + yield { 'start': max(time_begin, session_start), + 'end': min(time_end, session_end) } + + # TODO: data links + return [ x for x in gen_selection() ] diff --git a/backend/promis/backend_api/views.py b/backend/promis/backend_api/views.py index 72aa7ab..ef70eb7 100644 --- a/backend/promis/backend_api/views.py +++ b/backend/promis/backend_api/views.py @@ -26,8 +26,6 @@ from rest_framework.exceptions import NotAuthenticated, NotFound, MethodNotAllowed from rest_framework.renderers import JSONRenderer, BrowsableAPIRenderer -from django.contrib.gis.geos import GEOSGeometry - import unix_time import datetime @@ -264,7 +262,7 @@ def UserUpdate(request): # TODO: create a proper filterset # TODO: make use of Q() to help django understand the request class DataView(PromisViewSet): - serializer_class = serializer.MeasurementsSerializer + serializer_class = serializer.DataSerializer # TODO: only to prevent Django from asking for base_name queryset = models.Measurement.objects.all() @@ -274,13 +272,14 @@ def get_queryset(self): # Composing the filter filter_opts = {} + # Begin-End's are reversed: we are looking for session that overlap the time selection time_begin = self.request.query_params.get('time_begin') if time_begin: - filter_opts['session__time_begin__gte'] = unix_time.maketime(int(time_begin)) + filter_opts['session__time_end__gte'] = unix_time.maketime(int(time_begin)) time_end = self.request.query_params.get('time_end') if time_end: - filter_opts['session__time_end__lte'] = unix_time.maketime(int(time_end)) + filter_opts['session__time_begin__lte'] = unix_time.maketime(int(time_end)) # NOTE: channels and filters are combined as *AND* not *OR* at the moment # so they are mutually exclusive in a sense, but this won't be a problem for alpha @@ -296,15 +295,11 @@ def get_queryset(self): poly = self.request.query_params.get('polygon') - # Convert polygon to GEOS object as intersection doesn't auto-convert if poly: try: - poly = GEOSGeometry(poly, srid = 4326) filter_opts['session__geo_line__intersects'] = poly except ValueError: raise NotFound("Invalid WKT for polygon selection") # Applying the filter - qs = models.Measurement.objects.filter(**filter_opts) - - return qs + return models.Measurement.objects.filter(**filter_opts) diff --git a/backend/promis/classes/potential.py b/backend/promis/classes/potential.py index 53f1390..b5f8309 100644 --- a/backend/promis/classes/potential.py +++ b/backend/promis/classes/potential.py @@ -132,7 +132,7 @@ def fetch(self, daydir): ez_time_end = time_end # Generator for the orbit - line_gen = ( (y.lon, y.lat, y.alt) for _, y, _ in orbit.generate_orbit(orbit_path, time_start, time_end) ) + line_gen = ( (y.lon, y.lat, t) for t, y, _ in orbit.generate_orbit(orbit_path, time_start, time_end) ) # Converting time to python objects for convenience # This is the point where onboard time gets converted to the UTC time_start = unix_time.maketime(time_start) diff --git a/backend/promis/classes/test_set.py b/backend/promis/classes/test_set.py index d78c117..caa0eb4 100644 --- a/backend/promis/classes/test_set.py +++ b/backend/promis/classes/test_set.py @@ -35,7 +35,7 @@ def general_fetch(path, satellite_object): timemark = int(sess_name) ftp.cwd(sess_name) with ftp.xopen("orbit.csv") as fp: - line_gen = [ pt for pt in parsers.csv(fp) ] + line_gen = [ (pt[0],pt[1],t) for t, pt in enumerate(parsers.csv(fp)) ] time_start = unix_time.maketime(timemark) time_end = unix_time.maketime(timemark + len(line_gen)) # Orbit points are 1 per second time_dur = time_end - time_start diff --git a/backend/promis/orbit.py b/backend/promis/orbit.py index 1b77dc4..37faa46 100644 --- a/backend/promis/orbit.py +++ b/backend/promis/orbit.py @@ -126,4 +126,4 @@ def predict(t): res_vals[0] = ( res_vals[0] + 180 ) % 360 - 180 return OrbitPoint(*res_vals) - yield j, datapoints[j] if j in datapoints else predict(j), False if j in datapoints else True + yield j - orbit_start, datapoints[j] if j in datapoints else predict(j), False if j in datapoints else True diff --git a/frontend/app/components/CesiumMap.js b/frontend/app/components/CesiumMap.js index 66d2f7f..69dc163 100644 --- a/frontend/app/components/CesiumMap.js +++ b/frontend/app/components/CesiumMap.js @@ -458,8 +458,8 @@ export default class CesiumContainer extends Component { let cartesians = new Array(); /* data is [lat, lon, hgt] */ - data.forEach(function(point) { - cartesians.push(Cartesian3.fromDegrees(point[1], point[0], point[2] ? point[2] : 250000)); + data.forEach(function(point) { // TODO temporary disabling altitude because it stores time temporarily + cartesians.push(Cartesian3.fromDegrees(point[1], point[0], /*point[2] ? point[2] :*/ 250000)); }); return this.viewer.entities.add({