diff --git a/designs/icons-tcef.svg b/designs/icons-tcef.svg
index 0b965d0..f490152 100644
--- a/designs/icons-tcef.svg
+++ b/designs/icons-tcef.svg
@@ -246,7 +246,7 @@
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.24748737"
- inkscape:cx="680.72767"
+ inkscape:cx="-594.08486"
inkscape:cy="654.87036"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
@@ -272,64 +272,67 @@
inkscape:groupmode="layer"
id="layer1">
+ style="fill:#d45500;stroke-width:2.49893761"
+ transform="matrix(0.15677218,0,0,0.1664724,230.41495,-36.603316)"
+ id="g3415"
+ inkscape:export-filename="/mnt/md1/proyectos/dev/todos-contra-el-fuego-web/public/your-position.png"
+ inkscape:export-xdpi="24.116745"
+ inkscape:export-ydpi="24.116745">
+ style="fill:#d45500;stroke-width:2.49893761" />
+ style="fill:#d45500;stroke-width:2.49893761" />
+ style="fill:#d45500;stroke-width:2.49893761" />
+ style="fill:#d45500;stroke-width:2.49893761" />
+ style="fill:#d45500;stroke-width:2.49893761" />
+ style="fill:#d45500;stroke-width:2.49893761" />
+ style="fill:#d45500;stroke-width:2.49893761" />
+ style="fill:#d45500;stroke-width:2.49893761" />
+ style="fill:#d45500;stroke-width:2.49893761" />
+ style="fill:#d45500;stroke-width:2.49893761" />
+ style="fill:#d45500;stroke-width:2.49893761" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/imports/startup/client/geolocation.js b/imports/startup/client/geolocation.js
new file mode 100644
index 0000000..5d3a97a
--- /dev/null
+++ b/imports/startup/client/geolocation.js
@@ -0,0 +1,18 @@
+import { Meteor } from 'meteor/meteor';
+const geolocation = new ReactiveVar();
+
+Meteor.startup(function() {
+ Meteor.call("geo", function (error, response) {
+ if (error) {
+ console.warn(error);
+ } else {
+ var pos = [
+ response.location.latitude,
+ response.location.longitude
+ ];
+ geolocation.set(pos);
+ }
+ });
+});
+
+export default geolocation;
diff --git a/imports/startup/client/gkeys.js b/imports/startup/client/gkeys.js
new file mode 100644
index 0000000..ca38c40
--- /dev/null
+++ b/imports/startup/client/gkeys.js
@@ -0,0 +1,34 @@
+import { Meteor } from 'meteor/meteor';
+import i18n from 'i18next';
+
+const gmapkey = new ReactiveVar();
+
+Meteor.startup(function() {
+ Meteor.call('getMapKey', function (error, key) {
+ if (typeof key !== 'undefined') {
+ // console.log(key);
+ gmapkey.set(key);
+ } else {
+ callback(error, null)
+ }
+ })
+});
+
+var getGKeys = function (callback) {
+ Meteor.autorun(function() {
+ var key = gmapkey.get();
+ if (typeof key !== 'undefined') {
+ var script = document.createElement('script');
+ script.type = 'text/javascript';
+ script.onload = function () {
+ // console.log(key);
+ callback(null, key);
+ };
+ // https://stackoverflow.com/questions/28130114/google-maps-places-autocomplete-language-output
+ script.src = `https://maps.googleapis.com/maps/api/js?key=${key}&libraries=places&language=${i18n.language}`
+ document.body.appendChild(script);
+ }
+ });
+}
+
+export default getGKeys;
diff --git a/imports/startup/client/i18n.js b/imports/startup/client/i18n.js
index ecf5b83..2de5331 100644
--- a/imports/startup/client/i18n.js
+++ b/imports/startup/client/i18n.js
@@ -75,7 +75,7 @@ i18n.use(backend)
whitelist: false,
// whitelist: ['es', 'en'], // allowed languages
load: 'all', // es-ES -> es, en-US -> en
- debug: true,
+ debug: false,
ns: 'common',
defaultNS: 'common',
saveMissing: true, // if true seems it's fails to getResourceBundle
diff --git a/imports/startup/server/geolocation.js b/imports/startup/server/geolocation.js
index 2039b2f..dac9eb6 100644
--- a/imports/startup/server/geolocation.js
+++ b/imports/startup/server/geolocation.js
@@ -40,4 +40,11 @@ Meteor.methods({
})
return promise.await();
},
+ getMapKey: function () {
+ // http://meteorpedia.com/read/Environment_Variables
+ // https://developers.google.com/maps/documentation/javascript/get-api-key
+ // https://console.developers.google.com/
+ // export GMAPS_KEY=SomeGMapsKey
+ return process.env.GMAPS_KEY || Meteor.settings.gmaps.key;;
+ },
});
diff --git a/imports/ui/components/DistanceSlider/DistanceSlider.js b/imports/ui/components/DistanceSlider/DistanceSlider.js
new file mode 100644
index 0000000..f1b2e6f
--- /dev/null
+++ b/imports/ui/components/DistanceSlider/DistanceSlider.js
@@ -0,0 +1,96 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Trans, translate } from 'react-i18next';
+import Slider, { Range } from 'rc-slider';
+import Tooltip from 'rc-tooltip';
+// We can just import Slider or Range to reduce bundle size
+// import Slider from 'rc-slider/lib/Slider';
+// import Range from 'rc-slider/lib/Range';
+import 'rc-slider/assets/index.css';
+
+// https://www.npmjs.com/package/rc-slider
+const createSliderWithTooltip = Slider.createSliderWithTooltip;
+const Handle = Slider.Handle;
+
+const handle = (props) => {
+ const { value, dragging, index, ...restProps } = props;
+ return (
+
+
+
+ );
+};
+
+const wrapperStyle = { width: 400, margin: 50 };
+
+// https://github.com/react-component/slider/tree/master/examples
+
+class DistanceSlider extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ value: 10,
+ };
+ }
+
+ onSliderChange = (value) => {
+ // console.log(value);
+ this.setState({
+ value,
+ });
+ this.props.onChange(value);
+ }
+
+ onAfterChange = (value) => {
+ // console.log(`After change: ${value}`); //eslint-disable-line
+ this.props.onChange(value);
+ }
+
+ render() {
+ return (
+
+
+
¿A que distancia a la redonda quieres recibir notificaciones?
+
+
+ )
+ }
+}
+
+export default translate([], { wait: true })(DistanceSlider);
diff --git a/imports/ui/components/SelectionMap/SelectionMap.js b/imports/ui/components/SelectionMap/SelectionMap.js
new file mode 100644
index 0000000..5f8ddf0
--- /dev/null
+++ b/imports/ui/components/SelectionMap/SelectionMap.js
@@ -0,0 +1,119 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import { Map, TileLayer, Marker, Popup, CircleMarker, Circle} from 'react-leaflet';
+import Leaflet from 'leaflet';
+import { withTracker } from 'meteor/react-meteor-data';
+import { translate } from 'react-i18next';
+import geolocation from '/imports/startup/client/geolocation';
+import 'leaflet-graphicscale/dist/Leaflet.GraphicScale.min.css';
+import 'leaflet-graphicscale/dist/Leaflet.GraphicScale.min.js';
+
+const positionIcon = new Leaflet.Icon({
+ iconUrl: "/your-position.png",
+ /* shadowUrl: require('../public/marker-shadow.png'), */
+ iconSize: [50, 77], // size of the icon
+ /* shadowSize: [50, 64], // size of the shadow */
+ iconAnchor: [25, 82], // point of the icon which will correspond to marker's location
+ /* shadowAnchor: [4, 62], // the same for the shadow
+ * popupAnchor: [-3, -76]// point from which the popup should open relative to the iconAnchor*/
+})
+
+class SelectionMap extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ center: geolocation.get(),
+ marker: geolocation.get(),
+ zoom: 11,
+ draggable: true,
+ modified: false
+ };
+ // console.log(this.state);
+ }
+
+ componentDidUpdate = () => {
+ this.fit();
+ }
+
+ getMap = () => {
+ return this.refs.selectionMap.leafletElement;
+ }
+
+ toggleDraggable = () => {
+ this.setState({ draggable: !this.state.draggable })
+ }
+
+ updatePosition = () => {
+ const { lat, lng } = this.refs.marker.leafletElement.getLatLng()
+ this.setState({
+ marker: [ lat, lng ],
+ modified: true
+ });
+ }
+
+ fit() {
+ // console.log("fit!");
+ this.getMap().fitBounds(this.refs.distanceCircle.leafletElement.getBounds(), [70, 70]);
+ }
+
+ componentDidMount() {
+ this.addScale();
+ }
+
+ addScale = () => {
+ // https://www.npmjs.com/package/leaflet-graphicscale
+ const map = this.getMap();
+ var options = {
+ fill: 'fill',
+ showSubunits: true,
+ }
+ var graphicScale = L.control.graphicScale([options]).addTo(map);
+ }
+
+ render() {
+ this.state.center = !this.state.modified && this.props.lat?
+ [this.props.lat, this.props.lng]: this.state.center;
+ this.state.marker = !this.state.modified && this.props.lat?
+ [this.props.lat, this.props.lng]: this.state.marker;
+ this.state.distance = this.props.distance;
+ this.state.modified = false;
+ return (
+
+ { this.state && this.state.center &&
+ }
+
+ )
+ }
+}
+
+SelectionMap.propTypes = {
+ lat: PropTypes.number,
+ lng: PropTypes.number,
+ distance: PropTypes.number
+};
+
+export default translate([], { wait: true })(SelectionMap);
diff --git a/imports/ui/layouts/App/App.js b/imports/ui/layouts/App/App.js
index 00d7bd1..7382994 100644
--- a/imports/ui/layouts/App/App.js
+++ b/imports/ui/layouts/App/App.js
@@ -35,6 +35,7 @@ import { I18nextProvider } from 'react-i18next';
import i18n from '/imports/startup/client/i18n';
import '/imports/startup/client/piwik-start.js';
import ravenLogger from '/imports/startup/client/ravenLogger';
+import geolocation from '/imports/startup/client/geolocation';
// https://github.com/gadicc/meteor-blaze-react-component/
import Blaze from 'meteor/gadicc:blaze-react-component';
import createHistory from 'history/createBrowserHistory';
diff --git a/imports/ui/pages/FiresMap/FiresMap.js b/imports/ui/pages/FiresMap/FiresMap.js
index ef92256..18962c2 100644
--- a/imports/ui/pages/FiresMap/FiresMap.js
+++ b/imports/ui/pages/FiresMap/FiresMap.js
@@ -16,6 +16,7 @@ import LGeo from 'leaflet-geodesy';
import union from 'turf-union';
import 'leaflet-graphicscale/dist/Leaflet.GraphicScale.min.css';
import 'leaflet-graphicscale/dist/Leaflet.GraphicScale.min.js';
+import _ from 'lodash';
// https://stackoverflow.com/questions/35394577/leaflet-js-union-merge-circles
function unify(polyList) {
@@ -163,17 +164,22 @@ class FiresMap extends React.Component {
height.set(this.divElement.clientHeight);
width.set(this.divElement.clientWidth);
this.addScale();
+ const self = this;
+ this.handleViewportChangeDebounced = _.debounce(function (viewport) {
+ console.log(`Viewport changed: ${JSON.stringify(this.state.viewport)}`);
+ zoom.set(viewport.zoom);
+ lat.set(viewport.center[0]);
+ lng.set(viewport.center[1]);
+ self.state.viewport = viewport;
+ self.state.modified = true;
+ self.showSubsUnion(self.state.showSubsUnion);
+ }, 2000);
}
- onViewportChanged = viewport => {
- // console.log(`Viewport changed: ${JSON.stringify(this.state.viewport)}`);
- zoom.set(viewport.zoom);
- lat.set(viewport.center[0]);
- lng.set(viewport.center[1]);
- this.state.viewport = viewport;
- this.state.modified = true;
- this.showSubsUnion(this.state.showSubsUnion);
- }
+ // https://stackoverflow.com/questions/23123138/perform-debounce-in-react-js
+ onViewportChanged = (viewport) => {
+ this.handleViewportChangeDebounced(viewport);
+ };
onClickReset = () => {
// console.log("onclick");
@@ -297,7 +303,7 @@ class FiresMap extends React.Component {
- (*) Para preservar la privacidad de nuestros usuarios/as, los datos reflejados están aleatoriamente alterados y son solo orientativos.
+ (*) Para preservar la privacidad de nuestros usuarios/as, los datos reflejados están aleatoriamente alterados y son solo orientativos.
);
diff --git a/imports/ui/pages/Sandbox/Sandbox.js b/imports/ui/pages/Sandbox/Sandbox.js
index 3196cff..85d2931 100644
--- a/imports/ui/pages/Sandbox/Sandbox.js
+++ b/imports/ui/pages/Sandbox/Sandbox.js
@@ -1,58 +1,145 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { Button } from 'react-bootstrap';
+import { FormGroup, ControlLabel, Button, Row, Col, HelpBlock } from 'react-bootstrap';
import { Trans, translate } from 'react-i18next';
-import Slider, { Range } from 'rc-slider';
-import Tooltip from 'rc-tooltip';
-// We can just import Slider or Range to reduce bundle size
-// import Slider from 'rc-slider/lib/Slider';
-// import Range from 'rc-slider/lib/Range';
-import 'rc-slider/assets/index.css';
+import DistanceSlider from '/imports/ui/components/DistanceSlider/DistanceSlider';
+import SelectionMap from '/imports/ui/components/SelectionMap/SelectionMap';
+import getGKeys from '/imports/startup/client/gkeys';
+import PlacesAutocomplete, { geocodeByAddress, geocodeByPlaceId, getLatLng } from 'react-places-autocomplete'
+import update from 'immutability-helper';
+import { withTracker } from 'meteor/react-meteor-data';
-// https://www.npmjs.com/package/rc-slider
-const createSliderWithTooltip = Slider.createSliderWithTooltip;
-const Handle = Slider.Handle;
+class Sandbox extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ adddress: ''
+ };
+ self = this;
+ // this.handleSelect = this.handleSelect.bind(this)
+ // this.handleChange = this.handleChange.bind(this)
+ }
-const handle = (props) => {
- const { value, dragging, index, ...restProps } = props;
- return (
-
-
-
- );
-};
-const wrapperStyle = { width: 400, margin: 50 };
+ onChange = (address) => { this.setState({ address }) }
-const Sandbox = props => (
-
-
-
-);
+ handleSelect = (address) => {
+ geocodeByAddress(address)
+ .then((results) => getLatLng(results[0]))
+ .then(({ lat, lng }) => {
+ // console.log('Success Yay', { lat, lng })
+ const newState = update(this.state, {$merge: {lat: lat, lng: lng}});
+ // console.log(newState);
+ this.setState(newState);
+ // console.log(this.state);
+ })
+ .catch(error => {
+ console.log(error);
+ if (error === 'ZERO_RESULTS') {
+ console.log("No results");
+ }
+ });
+ }
-Sandbox.defaultProps = {
-};
+ onSliderChange = (value) => {
+ this.setState(update(this.state, {$merge: {distance: value}}));
+ }
+
+ render() {
+ // https://www.npmjs.com/package/react-places-autocomplete
+
+ // https://github.com/kenny-hibino/react-places-autocomplete/issues/103
+ const myStyles = {
+ autocompleteContainer: {
+ paddingBottom: '20px',
+ backgroundSize: 'auto 12px',
+ backgroundPosition: 'bottom left 10px',
+ backgroundRepeat: 'no-repeat',
+ backgroundImage: "url('https://maps.gstatic.com/mapfiles/api-3/images/powered-by-google-on-white3_hdpi.png')",
+ },
+ }
+
+ const AutocompleteItem = ({ formattedSuggestion }) => (
+
+ {' '}
+ {formattedSuggestion.mainText}{' '}
+ {formattedSuggestion.secondaryText}
+
)
+
+ // https://developers.google.com/places/web-service/search
+ // https://github.com/kenny-hibino/react-places-autocomplete/blob/master/demo/Demo.js
+ return (
+
+
+
+ { typeof this.props.gkey === 'string' &&
+
+
Suscríbete a alertas de fuegos
+
+
+ }
+
+
+
+
+
+
+
+
+
+
+ )
+ }
+}
Sandbox.propTypes = {
-};
+ gkey: PropTypes.string
+}
-export default translate([], { wait: true })(Sandbox);
+const gkey = new ReactiveVar();
+
+getGKeys(function(err, key) {
+ if (err) {
+ console.log(err);
+ } else {
+ gkey.set(key);
+ }
+});
+
+export default translate([], { wait: true }) (withTracker(() => {
+ return {
+ gkey: gkey.get()
+ };
+})(Sandbox));
diff --git a/package.json b/package.json
index 7197fbe..e423bf3 100644
--- a/package.json
+++ b/package.json
@@ -20,6 +20,7 @@
"i18next-browser-languagedetector": "^2.1.0",
"i18next-localstorage-cache": "^1.1.1",
"i18next-xhr-backend": "^1.5.0",
+ "immutability-helper": "^2.5.1",
"indexof": "0.0.1",
"jquery": "^2.2.4",
"jquery-validation": "^1.17.0",
@@ -42,6 +43,7 @@
"react-dom": "^15.6.1",
"react-i18next": "^6.1.0",
"react-leaflet": "^1.7.7",
+ "react-places-autocomplete": "^5.4.3",
"react-resize-detector": "^1.1.0",
"react-router-bootstrap": "^0.24.3",
"react-router-dom": "^4.2.2",
diff --git a/public/favicon.png b/public/favicon.png
index 2b26d3d..77a5179 100644
Binary files a/public/favicon.png and b/public/favicon.png differ
diff --git a/public/locales/es/common.json b/public/locales/es/common.json
index fa7ec65..88bbf46 100644
--- a/public/locales/es/common.json
+++ b/public/locales/es/common.json
@@ -119,5 +119,12 @@
"activeNeigFireInMapCount":
"En naranja, los fuegos notificados por nuestros usuarios/as recientemente.",
"Centrar el mapa en tu ubicación":
- "Centrar el mapa en tu ubicación"
+ "Centrar el mapa en tu ubicación",
+ "Resaltar en verde el área vigilada por nuestros usuarios/as":
+ "Resaltar en verde el área vigilada por nuestros usuarios/as",
+ "Resaltar los fuegos con un marcador":
+ "Resaltar los fuegos con un marcador",
+ "mapPrivacy":
+ "<0>Para preservar la privacidad de nuestros usuarios/as, los datos reflejados están aleatoriamente alterados y son solo orientativos.0> <0>Para preservar la privacidad de nuestros usuarios/as, los datos reflejados están aleatoriamente alterados y son solo orientativos.0>"
+
}
diff --git a/public/your-position.png b/public/your-position.png
new file mode 100644
index 0000000..c831c40
Binary files /dev/null and b/public/your-position.png differ