65 |
66 |
67 |
68 | );
69 | }
70 | } );
71 |
72 | export default Compass;
73 |
--------------------------------------------------------------------------------
/src/views/container.jsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2020 (c) Felix Palmer
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | */
8 | import React from 'react';
9 | // So we can inline the styles wrapping the engine, use this simple
10 | // component, to wrap other parts of app
11 | var Container = React.createClass( {
12 | render: function () {
13 | var outerStyle = {
14 | position: 'relative',
15 | height: '100%',
16 | overflow: 'hidden'
17 | };
18 | var innerStyle = {
19 | fontSize: '14px',
20 | color: '#fff',
21 | position: 'absolute',
22 | top: 0,
23 | bottom: 0,
24 | left: 0,
25 | right: 0
26 | };
27 | return (
28 |
29 |
30 | {this.props.children}
31 |
32 |
33 | );
34 | }
35 | } );
36 |
37 | export default Container;
38 |
--------------------------------------------------------------------------------
/src/views/credits.jsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2020 (c) Felix Palmer
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | */
8 | import React from 'react';
9 | import AppStore from '/stores/app';
10 | import uiFont from '/utils/uiFont';
11 | const style = {
12 | position : 'absolute',
13 | bottom : 0, right: 0,
14 | paddingTop: 1, paddingBottom: 1, paddingLeft: 4, paddingRight: 4,
15 | fontFamily : uiFont,
16 | textAlign : 'right',
17 | textTransform : 'none',
18 | color : 'white',
19 | background: 'rgba( 255, 255, 255, 0.1 )'
20 | };
21 | const pStyle = { color: 'white', fontSize : '6pt', margin: 0 };
22 |
23 | const Credits = React.createClass( {
24 | getInitialState: () => AppStore.getState().datasource,
25 | componentDidMount: function () {
26 | AppStore.listen( this.onStoreChange );
27 | this.onStoreChange( AppStore.getState() );
28 | },
29 | onStoreChange: function ( { datasource } ) {
30 | this.setState( datasource );
31 | },
32 | render: function () {
33 | let credits = [];
34 | if ( this.state.elevation && this.state.elevation.attribution ) {
35 | credits.push( this.state.elevation.attribution );
36 | }
37 | if ( this.state.imagery && this.state.imagery.attribution ) {
38 | credits.push( this.state.imagery.attribution );
39 | }
40 |
41 | // Inject link styles
42 | let aStyle = `color: ${pStyle.color}; font-size: ${pStyle.fontSize}; margin: ${pStyle.margin}; text-decoration: underline;`;
43 | credits = credits.map( credit =>
44 | credit.replace( /
49 | Data: { credits.map( credit =>
50 | <>
51 |
52 |
53 | > ) }
54 |
55 |
56 | );
57 | }
58 | } );
59 |
60 | export default Credits;
61 |
--------------------------------------------------------------------------------
/src/views/engine.jsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2020 (c) Felix Palmer
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | */
8 | import React from 'react';
9 | import ContainerStore from '/stores/container';
10 | import ErrorStore from '/stores/error';
11 | import renderer from '/renderer';
12 | import RenderActions from '/actions/render';
13 | var container = ContainerStore.getState().element;
14 | var Engine = React.createClass( {
15 | componentDidMount: function () {
16 | var domNode = this.base || this.getDOMNode();
17 | domNode.appendChild( container );
18 | container.appendChild( renderer.domElement );
19 | setTimeout( function () { RenderActions.containerMounted(); }, 0 );
20 | ErrorStore.listen( this.onError );
21 | this.setState( this.getInitialState() );
22 | },
23 | getInitialState: function () {
24 | return { error: ErrorStore.getState().message }
25 | },
26 | onError: function ( state ) {
27 | if ( state.displayErrors ) {
28 | this.setState( { error: state.message } );
29 | }
30 | },
31 | render: function () {
32 | var style = {
33 | width: '100%',
34 | height: '100%',
35 | touchAction: 'none'
36 | }
37 | if ( this.state.error ) {
38 | var errorContainerStyle = {
39 | position: 'absolute',
40 | width: '100%',
41 | height: '100%',
42 | zIndex: 10,
43 | background: 'black',
44 | };
45 | var errorStyle = {
46 | padding: 10,
47 | textAlign: 'center'
48 | };
49 | return (
50 |
51 |
52 |
Error
53 |
{this.state.error}
54 |
55 |
56 | );
57 | }
58 | return
;
59 | }
60 | } );
61 |
62 | export default Engine;
63 |
--------------------------------------------------------------------------------
/src/views/engineControlsBottom.jsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2020 (c) Felix Palmer
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | */
8 | import React from 'react';
9 | import ButtonRow from '/views/buttonRow.jsx';
10 | import CurrentLocationActions from '/actions/currentLocation';
11 | import UserInterfaceStore from '/stores/userInterface';
12 |
13 | // Listen for geolocation
14 | var userLocation = null;
15 | var registered = false;
16 | if ( 'geolocation' in navigator ) {
17 | var toggleTracking = function () {
18 | if ( !registered ) {
19 | navigator.geolocation.watchPosition( CurrentLocationActions.panToPosition );
20 | registered = true;
21 | }
22 |
23 | CurrentLocationActions.toggleTracking();
24 | }
25 | userLocation = {
26 | icon : 'b1 fa fa-location-arrow',
27 | action : toggleTracking
28 | }
29 | }
30 |
31 | var EngineControlsBottom = React.createClass( {
32 | getInitialState: function () {
33 | return UserInterfaceStore.getState();
34 | },
35 | componentDidMount: function () {
36 | UserInterfaceStore.listen( this.onStoreChange );
37 | this.setState( this.getInitialState() );
38 | },
39 | onStoreChange: function ( storeState ) {
40 | this.setState( storeState );
41 | },
42 | shouldComponentUpdate: function ( nextProps, nextState ) {
43 | return this.state.userLocationControlVisible !== nextState.userLocationControlVisible;
44 | },
45 | render: function () {
46 | var buttons = [];
47 | if ( this.state.userLocationControlVisible ) {
48 | buttons.push(
);
49 | }
50 | var style = {
51 | position: 'absolute',
52 | top: 'initial',
53 | bottom: 15,
54 | right: 8
55 | }
56 | return (
57 |
58 | {buttons}
59 |
60 | );
61 | }
62 | } );
63 |
64 | export default EngineControlsBottom;
65 |
--------------------------------------------------------------------------------
/src/views/logo.jsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2020 (c) Felix Palmer
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | */
8 | import React from 'react';
9 | import logoStyle from '/views/logoStyle.css';
10 | var Logo = React.createClass( {
11 | render: function () {
12 | var style = {};
13 | if ( this.props.color ) {
14 | style.background = this.props.color;
15 | }
16 | return (
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | );
25 | }
26 | } );
27 |
28 | export default Logo;
29 |
--------------------------------------------------------------------------------
/src/views/logoStyle.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2020 (c) Felix Palmer
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | */
8 | /* Animation */
9 | @keyframes procedural-shrink-1 {
10 | 0% { transform: rotate(90deg) scale(1) translate(-60%, -60%); }
11 | 100% { transform: rotate(180deg) scale(0.5) translate(-70%, -70%); }
12 | }
13 | @keyframes procedural-shrink-2 {
14 | 0% { transform: rotate(180deg) scale(0.5) translate(-70%, -70%); }
15 | 100% { transform: rotate(270deg) scale(0.25) translate(-90%, -90%); }
16 | }
17 | @keyframes procedural-shrink-3 {
18 | 0% { transform: rotate(270deg) scale(0.25) translate(-90%, -90%); }
19 | 100% { transform: rotate(360deg) scale(0.125) translate(-130%, -130%); }
20 | }
21 | @keyframes procedural-shrink-4 {
22 | 0% {
23 | transform: rotate(0deg) scale(0.125) translate(-130%, -130%);
24 | animation-timing-function: ease-in;
25 | }
26 | 50% {
27 | transform: rotate(0deg) scale(0.125) translate(-130%, -130%);
28 | animation-timing-function: ease-in;
29 | }
30 | 100% {
31 | transform: rotate(90deg) scale(1) translate(-60%, -60%);
32 | }
33 | }
34 |
35 | /* Shapes */
36 | .square {
37 | position: absolute;
38 | background: #666;
39 | width: 100%;
40 | height: 100%;
41 | box-shadow: 0 0 10px rgba(0, 0, 0, 0);
42 | }
43 |
44 | .square-1 { animation: procedural-shrink-1 1.8s infinite cubic-bezier(0.6, -0.1337, 0.735, 0.045); }
45 | .square-2 { animation: procedural-shrink-2 1.8s infinite cubic-bezier(0.6, -0.1337, 0.735, 0.045); }
46 | .square-3 { animation: procedural-shrink-3 1.8s infinite cubic-bezier(0.6, -0.1337, 0.735, 0.045); }
47 | .square-4 { animation: procedural-shrink-4 1.8s infinite cubic-bezier(0.6, -0.1337, 0.735, 0.045); }
48 |
49 | #logo-container {
50 | position: relative;
51 | left: -45%;
52 | width: 100%;
53 | height: 100%;
54 | -webkit-touch-callout: none;
55 | -webkit-user-select: none;
56 | -khtml-user-select: none;
57 | -moz-user-select: none;
58 | -ms-user-select: none;
59 | user-select: none;
60 | }
61 |
--------------------------------------------------------------------------------
/src/views/poweredBy.jsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2020 (c) Felix Palmer
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | */
8 | import React from 'react';
9 | import logo from '/logo.svg';
10 | var PoweredBy = React.createClass( {
11 | render: function () {
12 | var containerStyle = {
13 | position : 'absolute',
14 | padding : '11px 8px',
15 | bottom : 0,
16 | opacity : 0.9,
17 | WebkitTouchCallout : 'none',
18 | WebkitUserSelect : 'none',
19 | KhtmlUserSelect : 'none',
20 | MozUserSelect : 'none',
21 | MsUserSelect : 'none',
22 | userSelect : 'none'
23 | };
24 | var logoStyle = {
25 | backgroundImage: logo,
26 | backgroundRepeat: 'no-repeat',
27 | display: 'block',
28 | height: 40,
29 | width: 40
30 | };
31 | var link = 'https://github.com/felixpalmer/procedural-gl-js';
32 | return (
33 |
36 | );
37 | }
38 | } );
39 |
40 | export default PoweredBy;
41 |
--------------------------------------------------------------------------------
/src/views/root.jsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2020 (c) Felix Palmer
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 | */
8 | import React from 'react';
9 | import app from '/app';
10 | import AppStore from '/stores/app';
11 | import Compass from '/views/compass.jsx';
12 | import ConfigActions from '/actions/config';
13 | import Container from '/views/container.jsx';
14 | import Credits from '/views/credits.jsx';
15 | import Engine from '/views/engine.jsx';
16 | import EngineControlsBottom from '/views/engineControlsBottom.jsx';
17 | import EngineControlsTop from '/views/engineControlsTop.jsx';
18 | import EnvironmentStore from '/stores/environment';
19 | import PoweredBy from '/views/poweredBy.jsx';
20 | import RenderActions from '/actions/render';
21 | import UserActions from '/actions/user';
22 |
23 | // Root React component
24 | var App = React.createClass( {
25 | render: function () {
26 | return (
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | );
36 | }
37 | } );
38 |
39 | var renderApp = function ( state ) {
40 | if ( state.appContainer ) {
41 | React.render( , state.appContainer );
42 | }
43 | };
44 | AppStore.listen( renderApp );
45 |
46 | ConfigActions.configureCamera( {
47 | minDistance: 500,
48 | minHeight: 250,
49 | zoomInDuration: 0.8,
50 | zoomOutDuration: 1
51 | } );
52 |
53 | app.init();
54 |
--------------------------------------------------------------------------------
/webserver.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import SimpleHTTPServer
3 | import SocketServer
4 |
5 | # Simple web server with caching disabled (useful for development)
6 | class MyHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
7 | def end_headers(self):
8 | self.send_my_headers()
9 | SimpleHTTPServer.SimpleHTTPRequestHandler.end_headers(self)
10 |
11 | def send_my_headers(self):
12 | self.send_header("Cache-Control", "no-cache, no-store, must-revalidate")
13 | self.send_header("Access-Control-Allow-Origin", "*")
14 | self.send_header("Pragma", "no-cache")
15 | self.send_header("Expires", "0")
16 |
17 |
18 | if __name__ == '__main__':
19 | port = 3456
20 | httpd = SocketServer.TCPServer(("", port), MyHTTPRequestHandler)
21 | print "Started server on port %s" % port
22 | httpd.serve_forever()
23 |
--------------------------------------------------------------------------------