API & Client: use of calculated bounds from API to display map
This commit is contained in:
parent
e4a65f4c79
commit
12595040d5
2
Makefile
2
Makefile
@ -53,7 +53,7 @@ test-python:
|
|||||||
test-python-xml:
|
test-python-xml:
|
||||||
$(PYTEST) mpwo_api --cov-config .coveragerc --cov=mpwo_api --cov-report xml
|
$(PYTEST) mpwo_api --cov-config .coveragerc --cov=mpwo_api --cov-report xml
|
||||||
|
|
||||||
update-cov:
|
update-cov: test-python-xml
|
||||||
$(COV) -r coverage.xml
|
$(COV) -r coverage.xml
|
||||||
|
|
||||||
upgrade-db:
|
upgrade-db:
|
||||||
|
28
mpwo_api/migrations/versions/92adde6ac0d0_.py
Normal file
28
mpwo_api/migrations/versions/92adde6ac0d0_.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
"""add 'bounds' column to 'Activity' table
|
||||||
|
|
||||||
|
Revision ID: 92adde6ac0d0
|
||||||
|
Revises: dd73d23a7a3d
|
||||||
|
Create Date: 2018-05-14 14:25:04.889189
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy.dialects import postgresql
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '92adde6ac0d0'
|
||||||
|
down_revision = 'dd73d23a7a3d'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.add_column('activities', sa.Column('bounds', postgresql.ARRAY(sa.Float()), nullable=True))
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_column('activities', 'bounds')
|
||||||
|
# ### end Alembic commands ###
|
@ -1,6 +1,7 @@
|
|||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from mpwo_api import db
|
from mpwo_api import db
|
||||||
|
from sqlalchemy.dialects import postgresql
|
||||||
from sqlalchemy.ext.hybrid import hybrid_property
|
from sqlalchemy.ext.hybrid import hybrid_property
|
||||||
from sqlalchemy.types import Enum
|
from sqlalchemy.types import Enum
|
||||||
|
|
||||||
@ -68,6 +69,7 @@ class Activity(db.Model):
|
|||||||
ascent = db.Column(db.Numeric(5, 2), nullable=True) # meters
|
ascent = db.Column(db.Numeric(5, 2), nullable=True) # meters
|
||||||
max_speed = db.Column(db.Numeric(5, 2), nullable=True) # km/h
|
max_speed = db.Column(db.Numeric(5, 2), nullable=True) # km/h
|
||||||
ave_speed = db.Column(db.Numeric(5, 2), nullable=True) # km/h
|
ave_speed = db.Column(db.Numeric(5, 2), nullable=True) # km/h
|
||||||
|
bounds = db.Column(postgresql.ARRAY(db.Float), nullable=True)
|
||||||
segments = db.relationship('ActivitySegment',
|
segments = db.relationship('ActivitySegment',
|
||||||
lazy=True,
|
lazy=True,
|
||||||
cascade='all, delete',
|
cascade='all, delete',
|
||||||
@ -110,6 +112,7 @@ class Activity(db.Model):
|
|||||||
"max_speed": float(self.max_speed) if self.max_speed else None,
|
"max_speed": float(self.max_speed) if self.max_speed else None,
|
||||||
"ave_speed": float(self.ave_speed) if self.ave_speed else None,
|
"ave_speed": float(self.ave_speed) if self.ave_speed else None,
|
||||||
"with_gpx": self.gpx is not None,
|
"with_gpx": self.gpx is not None,
|
||||||
|
"bounds": [float(bound) for bound in self.bounds] if self.bounds else [], # noqa
|
||||||
"segments": [segment.serialize() for segment in self.segments]
|
"segments": [segment.serialize() for segment in self.segments]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,6 +52,7 @@ def create_activity(
|
|||||||
|
|
||||||
if gpx_data:
|
if gpx_data:
|
||||||
new_activity.gpx = gpx_data['filename']
|
new_activity.gpx = gpx_data['filename']
|
||||||
|
new_activity.bounds = gpx_data['bounds']
|
||||||
update_activity_data(new_activity, gpx_data)
|
update_activity_data(new_activity, gpx_data)
|
||||||
else:
|
else:
|
||||||
new_activity.moving = duration
|
new_activity.moving = duration
|
||||||
|
@ -18,6 +18,21 @@ def assert_activity_data_with_gpx(data):
|
|||||||
assert data['data']['activities'][0]['moving'] == '0:04:10'
|
assert data['data']['activities'][0]['moving'] == '0:04:10'
|
||||||
assert data['data']['activities'][0]['pauses'] is None
|
assert data['data']['activities'][0]['pauses'] is None
|
||||||
assert data['data']['activities'][0]['with_gpx'] is True
|
assert data['data']['activities'][0]['with_gpx'] is True
|
||||||
|
assert len(data['data']['activities'][0]['segments']) == 1
|
||||||
|
|
||||||
|
segment = data['data']['activities'][0]['segments'][0]
|
||||||
|
assert segment['activity_id'] == 1
|
||||||
|
assert segment['segment_id'] == 0
|
||||||
|
assert segment['duration'] == '0:04:10'
|
||||||
|
assert segment['ascent'] == 0.4
|
||||||
|
assert segment['ave_speed'] == 4.6
|
||||||
|
assert segment['descent'] == 23.4
|
||||||
|
assert segment['distance'] == 0.32
|
||||||
|
assert segment['max_alt'] == 998.0
|
||||||
|
assert segment['max_speed'] == 5.09
|
||||||
|
assert segment['min_alt'] == 975.0
|
||||||
|
assert segment['moving'] == '0:04:10'
|
||||||
|
assert segment['pauses'] is None
|
||||||
|
|
||||||
|
|
||||||
def assert_activity_data_wo_gpx(data):
|
def assert_activity_data_wo_gpx(data):
|
||||||
|
@ -23,8 +23,12 @@ class ActivityMap extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { gpxContent } = this.props
|
const { activity, gpxContent } = this.props
|
||||||
const { jsonData, bounds } = getGeoJson(gpxContent)
|
const { jsonData } = getGeoJson(gpxContent)
|
||||||
|
const bounds = [
|
||||||
|
[activity.bounds[0], activity.bounds[1]],
|
||||||
|
[activity.bounds[2], activity.bounds[3]]
|
||||||
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@ -32,7 +36,7 @@ class ActivityMap extends React.Component {
|
|||||||
<Map
|
<Map
|
||||||
zoom={this.state.zoom}
|
zoom={this.state.zoom}
|
||||||
bounds={bounds}
|
bounds={bounds}
|
||||||
boundsOptions={{ padding: [50, 50] }}
|
boundsOptions={{ padding: [20, 20] }}
|
||||||
>
|
>
|
||||||
<TileLayer
|
<TileLayer
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import togeojson from '@mapbox/togeojson'
|
import togeojson from '@mapbox/togeojson'
|
||||||
import { format, parse } from 'date-fns'
|
import { format, parse } from 'date-fns'
|
||||||
import bbox from 'geojson-bbox'
|
|
||||||
|
|
||||||
export const apiUrl = `${process.env.REACT_APP_API_URL}/api/`
|
export const apiUrl = `${process.env.REACT_APP_API_URL}/api/`
|
||||||
export const thunderforestApiKey = `${
|
export const thunderforestApiKey = `${
|
||||||
@ -19,18 +18,12 @@ export const generateIds = arr => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const getGeoJson = gpxContent => {
|
export const getGeoJson = gpxContent => {
|
||||||
let jsonData, bboxCorners
|
let jsonData
|
||||||
let bounds = [[0, 0], [0, 0]]
|
|
||||||
if (gpxContent) {
|
if (gpxContent) {
|
||||||
const gpx = new DOMParser().parseFromString(gpxContent, 'text/xml')
|
const gpx = new DOMParser().parseFromString(gpxContent, 'text/xml')
|
||||||
jsonData = togeojson.gpx(gpx)
|
jsonData = togeojson.gpx(gpx)
|
||||||
bboxCorners = bbox(jsonData)
|
|
||||||
bounds = [
|
|
||||||
[bboxCorners[1], bboxCorners[0]],
|
|
||||||
[bboxCorners[3], bboxCorners[2]]
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
return { jsonData, bounds }
|
return { jsonData }
|
||||||
}
|
}
|
||||||
|
|
||||||
export const formatActivityDate = activityDateTime => {
|
export const formatActivityDate = activityDateTime => {
|
||||||
|
5
package-lock.json
generated
5
package-lock.json
generated
@ -4687,11 +4687,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
|
||||||
"integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc="
|
"integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc="
|
||||||
},
|
},
|
||||||
"geojson-bbox": {
|
|
||||||
"version": "0.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/geojson-bbox/-/geojson-bbox-0.0.0.tgz",
|
|
||||||
"integrity": "sha1-6gf7YmEMzCmYeWAaVbqIQTm8G+g="
|
|
||||||
},
|
|
||||||
"get-caller-file": {
|
"get-caller-file": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz",
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
"@mapbox/togeojson": "^0.16.0",
|
"@mapbox/togeojson": "^0.16.0",
|
||||||
"chalk": "2.4.1",
|
"chalk": "2.4.1",
|
||||||
"date-fns": "^1.29.0",
|
"date-fns": "^1.29.0",
|
||||||
"geojson-bbox": "^0.0.0",
|
|
||||||
"history": "^4.7.2",
|
"history": "^4.7.2",
|
||||||
"leaflet": "^1.3.1",
|
"leaflet": "^1.3.1",
|
||||||
"react": "^16.3.2",
|
"react": "^16.3.2",
|
||||||
|
@ -3803,10 +3803,6 @@ generate-object-property@^1.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-property "^1.0.0"
|
is-property "^1.0.0"
|
||||||
|
|
||||||
geojson-bbox@^0.0.0:
|
|
||||||
version "0.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/geojson-bbox/-/geojson-bbox-0.0.0.tgz#ea07fb62610ccc299879601a55ba884139bc1be8"
|
|
||||||
|
|
||||||
get-caller-file@^1.0.1:
|
get-caller-file@^1.0.1:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5"
|
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5"
|
||||||
|
Loading…
Reference in New Issue
Block a user