diff options
author | Alexander NeonXP Kiryukhin <a.kiryukhin@mail.ru> | 2019-06-10 03:15:51 +0300 |
---|---|---|
committer | Alexander NeonXP Kiryukhin <a.kiryukhin@mail.ru> | 2019-06-10 03:15:51 +0300 |
commit | 06645cdac4184f5c1cf50f2a4b94be3d72d634f0 (patch) | |
tree | 1eb62ec1d2275ae6feb90cfceedb7b93257005b0 /src | |
parent | 4b01d81d3daed894bc93f77dbbbe5501a4552447 (diff) |
More improvements
Diffstat (limited to 'src')
-rw-r--r-- | src/Actions/actions.ts | 2 | ||||
-rw-r--r-- | src/Actions/entity.ts | 13 | ||||
-rw-r--r-- | src/Actions/settings.ts | 11 | ||||
-rw-r--r-- | src/Api/api.ts | 8 | ||||
-rw-r--r-- | src/Components/Map.tsx | 29 | ||||
-rw-r--r-- | src/Components/MapObjects.tsx | 128 | ||||
-rw-r--r-- | src/Components/MapOverlay.tsx | 53 | ||||
-rw-r--r-- | src/Components/Settings.tsx | 32 | ||||
-rw-r--r-- | src/Store/store.ts | 50 | ||||
-rw-r--r-- | src/constants.ts | 4 | ||||
-rw-r--r-- | src/helper.js | 23 |
11 files changed, 265 insertions, 88 deletions
diff --git a/src/Actions/actions.ts b/src/Actions/actions.ts index 988ba65..62abb96 100644 --- a/src/Actions/actions.ts +++ b/src/Actions/actions.ts @@ -1,9 +1,11 @@ import auth from './auth' import entity from './entity' +import settings from './settings'; export const actions = { ...auth, ...entity, + ...settings, } export default actions
\ No newline at end of file diff --git a/src/Actions/entity.ts b/src/Actions/entity.ts index d23022a..8b01ea6 100644 --- a/src/Actions/entity.ts +++ b/src/Actions/entity.ts @@ -9,7 +9,7 @@ const entity = { 'update': (region: Region, width: number, refresh: boolean) => async (dispatch, getStore) => { const store = getStore() const queue = store.entities.loadQueue - const loadedAlready = refresh ? + const loadedAlready = !refresh ? [ ...Object.keys(store.entities.portals), ...queue, @@ -18,6 +18,7 @@ const entity = { dispatch(entity.setLoadQueue([...queue, ...tiles])) setImmediate(() => dispatch(entity.loadRutine())) }, + 'loadRutine': () => async (dispatch, getStore) => { const store = getStore() const queue = store.entities.loadQueue @@ -27,6 +28,8 @@ const entity = { loadTiles(chunk, params) .then(({ result, failed }) => { const queue = store.entities.loadQueue + dispatch(entity.linksGC(result.linksByTile)) + dispatch(entity.fieldsGC(result.fieldsByTile)) dispatch(entity.portalsSet(result.portals)) dispatch(entity.linksSet(result.links)) dispatch(entity.fieldsSet(result.fields)) @@ -73,6 +76,14 @@ const entity = { type: 'linksSet', links, }), + 'linksGC': (links: { [tile: string]: string[] }) => ({ + type: 'linksGC', + links, + }), + 'fieldsGC': (fields: { [tile: string]: string[] }) => ({ + type: 'fieldsGC', + fields, + }), 'fieldsSet': (fields: { [guid: string]: Field }) => ({ type: 'fieldsSet', fields, diff --git a/src/Actions/settings.ts b/src/Actions/settings.ts new file mode 100644 index 0000000..8dfc3fc --- /dev/null +++ b/src/Actions/settings.ts @@ -0,0 +1,11 @@ +const settings = { + 'setLevelFrom': (level: Number) => ({ + type: 'setLevelFrom', + level + }), + 'setLevelTo': (level: Number) => ({ + type: 'setLevelTo', + level + }) +} +export default settings
\ No newline at end of file diff --git a/src/Api/api.ts b/src/Api/api.ts index 4c83252..1fd9435 100644 --- a/src/Api/api.ts +++ b/src/Api/api.ts @@ -153,11 +153,11 @@ const pointToTileId = (x: number, y: number, params: TileParameters): string => return params.zoom + "_" + x + "_" + y + "_" + params.level + "_8_100"; } -const getZoomByRegion = (width: number, region: Region): number => { +export const getZoomByRegion = (width: number, region: Region): number => { return Math.ceil(Math.log2(360 * ((width / 256) / region.longitudeDelta))) + 1 } -const getDataZoomForMapZoom = (zoom: number): number => { +export const getDataZoomForMapZoom = (zoom: number): number => { if (zoom > 21) { zoom = 21; } @@ -223,9 +223,9 @@ const getEntities = (tileKeys: string[], params: RequestParams): Promise<GetEnti "body": JSON.stringify({ tileKeys, v: params.v }), "method": "POST", }) - return timeout(10000, promise.then(r => { + return timeout(30000, promise.then(async r => { if (r.status != 200) { - return { result: { map: {} } } + throw Error(r.statusText) } return r.json() })) diff --git a/src/Components/Map.tsx b/src/Components/Map.tsx index bb0b660..db0bed9 100644 --- a/src/Components/Map.tsx +++ b/src/Components/Map.tsx @@ -1,6 +1,6 @@ import React, { Component } from 'react'; import { StyleSheet, View, Dimensions, ActivityIndicator } from 'react-native'; -import MapView, { Marker, Region, UrlTile } from 'react-native-maps'; +import MapView, { Marker, Region } from 'react-native-maps'; import { connect } from 'redux-su'; import { NavigationActions } from 'react-navigation'; @@ -10,6 +10,7 @@ import PortalPanel from './PortalPanel'; import { getBottomSpace } from '../helper'; import actions from '../Actions/actions'; import { LatLng } from '../Api/interfaces'; +import { getZoomByRegion, getDataZoomForMapZoom } from '../Api/api'; const { width, height } = Dimensions.get("screen") const draggableRange = { @@ -32,6 +33,7 @@ class Map extends Component<Props, State> { this.state = { user: undefined, region: null, + dataZoom: 15, } } componentDidMount() { @@ -63,7 +65,9 @@ class Map extends Component<Props, State> { ) } onRegionChange = (region: Region) => { - this.setState({ region }) + const zoom = getZoomByRegion(width, region) + const dataZoom = getDataZoomForMapZoom(zoom); + this.setState({ region, dataZoom }) setImmediate(() => this.load(false)) } @@ -79,7 +83,15 @@ class Map extends Component<Props, State> { } onPortalClick = (guid: string, coords: LatLng) => { - this.setState({ selectedPortal: { guid, coords } }) + if (this.state.selectedPortal && this.state.selectedPortal.guid == guid) { + this.setState({ selectedPortal: false }) + } else { + this.setState({ selectedPortal: { guid, coords } }) + } + } + + onPortalDismiss = () => { + this.setState({ selectedPortal: false }) } onOpenPortal = (guid: string, coords: LatLng) => { @@ -115,8 +127,13 @@ class Map extends Component<Props, State> { shouldRasterizeIOS renderToHardwareTextureAndroid > - <MapObjects onPortalClick={this.onPortalClick} region={this.state.region || initialRegion} /> - {this.state.selectedPortal && <Marker cluster={false} coordinate={this.state.selectedPortal.coords} />} + <MapObjects + onPortalClick={this.onPortalClick} + region={this.state.region || initialRegion} + levels={this.props.settings.filterLevel} + zoom={this.state.dataZoom} + /> + {this.state.selectedPortal && <Marker cluster={false} coordinate={this.state.selectedPortal.coords} onPress={this.onPortalDismiss} />} </MapView> <MapOverlay goToMe={this.goToMe} @@ -136,4 +153,4 @@ const styles = StyleSheet.create({ }, }); -export default connect({ 'entities': 'entities' }, actions)(Map)
\ No newline at end of file +export default connect({ 'entities': 'entities', 'settings': 'settings' }, actions)(Map)
\ No newline at end of file diff --git a/src/Components/MapObjects.tsx b/src/Components/MapObjects.tsx index 4b11225..2a5e1df 100644 --- a/src/Components/MapObjects.tsx +++ b/src/Components/MapObjects.tsx @@ -4,7 +4,27 @@ import { Polyline, Polygon, Marker, Region } from 'react-native-maps'; import Icon from 'react-native-vector-icons/FontAwesome'; import { Link, Portal, Field } from '../Api/types'; import { connect } from 'react-redux'; -import { COLORS, COLORS_LVL } from '../constants'; +import { COLORS, COLORS_LVL, DEFAULT_ZOOM_TO_LEVEL, DEFAULT_ZOOM_TO_LINK_LENGTH } from '../constants'; +import { calcCrow } from '../helper'; +const PORTALS = { + "N1": require('../../assets/N.png'), + "R1": require('../../assets/R1.png'), + "R2": require('../../assets/R2.png'), + "R3": require('../../assets/R3.png'), + "R4": require('../../assets/R4.png'), + "R5": require('../../assets/R5.png'), + "R6": require('../../assets/R6.png'), + "R7": require('../../assets/R7.png'), + "R8": require('../../assets/R8.png'), + "E1": require('../../assets/E1.png'), + "E2": require('../../assets/E2.png'), + "E3": require('../../assets/E3.png'), + "E4": require('../../assets/E4.png'), + "E5": require('../../assets/E5.png'), + "E6": require('../../assets/E6.png'), + "E7": require('../../assets/E7.png'), + "E8": require('../../assets/E8.png'), +} const fillPortalColor = { "R": "rgba(2, 140, 227, 0.5)", "E": "rgba(38, 205, 30, 0.5)", "N": "rgba(139, 0, 255, 0.5)" } // const fillPortalColor = { "R": COLORS[1], "E": COLORS[2], "N": COLORS[0] } @@ -13,11 +33,12 @@ const fractColor = { "R": "#028ce3", "E": "#26cd1e", "N": "#8b00ff" } const fieldColor = { "R": "rgba(2, 140, 227, 0.1)", "E": "rgba(38, 205, 30, 0.1)", "N": "rgba(139, 0, 255, 0.1)" } type Props = { - portals: { [guid: string]: Portal } - links: { [guid: string]: Link } - fields: { [guid: string]: Field } + portals: Portal[] + links: Link[] + fields: Field[] region: Region onPortalClick: Function + zoom: Number } class MapObjects extends PureComponent<Props> { @@ -31,60 +52,18 @@ class MapObjects extends PureComponent<Props> { const fields = this.props.fields const links = this.props.links const zoom = this.props.zoom - const renderPortal = Object.keys(portals) - .filter(guid => portals[guid].coords.latitude > lat1 && portals[guid].coords.latitude < lat2 && portals[guid].coords.longitude > lng1 && portals[guid].coords.longitude < lng2) - .map(guid => this.drawPortal(guid, portals[guid], this.props.onPortalClick)) - const renderLink = Object.keys(links) - .filter(guid => { - const l1 = links[guid].coords[0].latitude - const l2 = links[guid].coords[1].latitude - const n1 = links[guid].coords[0].longitude - const n2 = links[guid].coords[1].longitude - return !(l1 < lat1 && l2 < lat1 || l1 > lat2 && l2 > lat2 || n1 < lng1 && n2 < lng1 || n1 > lng2 && n2 > lng2) - }) - .map(guid => this.drawLink(guid, links[guid])) - const renderField = Object.keys(fields) - .filter(guid => { - const l1 = fields[guid].coords[0].latitude - const l2 = fields[guid].coords[1].latitude - const l3 = fields[guid].coords[2].latitude - const n1 = fields[guid].coords[0].longitude - const n2 = fields[guid].coords[1].longitude - const n3 = fields[guid].coords[2].longitude - return !( - l1 < lat1 && l2 < lat1 && l3 < lat1 || l1 > lat2 && l2 > lat2 && l3 > lat2 || - n1 < lng1 && n2 < lng1 && n3 < lng1 || n1 > lng2 && n2 > lng2 && n3 > lng2 - ) - }) - .map(guid => this.drawField(guid, fields[guid])) - - if (zoom <= 14) { - return [...renderField, ...renderLink] - } else { - return [...renderField, ...renderLink, ...renderPortal] - } + const renderPortal = this.props.renderPortal.map(guid => this.drawPortal(guid, portals[guid], this.props.onPortalClick)) + const renderLink = this.props.renderLink.map(guid => this.drawLink(guid, links[guid])) + const renderField = this.props.renderField.map(guid => this.drawField(guid, fields[guid])) + return [...renderField, ...renderLink, ...renderPortal] } drawPortal = (guid: string, entity: Portal, onPortalClick: Function) => { - const size = 20 return (<Marker key={guid} coordinate={entity.coords} onPress={() => onPortalClick(guid, entity.coords)} - > - {/* <Icon name={entity.fraction == "N" ? "circle-o" : "circle"} color={fillPortalColor[entity.fraction]} size={size} /> */} - <View style={{ - borderWidth: 2, - height: size, - width: size, - borderRadius: size / 2, - borderColor: COLORS_LVL[entity.level], - backgroundColor: fillPortalColor[entity.fraction], - justifyContent: 'center', - alignItems: 'center', - }}> - <Text style={{ fontWeight: 'bold' }}>{entity.level}</Text> - </View> - </Marker>); + image={PORTALS[entity.fraction + entity.level]} + />); } drawField = (guid: string, entity: Field) => { return <Polygon @@ -109,4 +88,47 @@ const styles = StyleSheet.create({ }); -export default connect((store, props: Props) => ({ portals: store.entities.portals, fields: store.entities.fields, links: store.entities.links }), {})(MapObjects)
\ No newline at end of file +export default connect((store, props: Props) => { + const region = props.region + const lat1 = region.latitude - region.latitudeDelta / 2 + const lat2 = region.latitude + region.latitudeDelta / 2 + const lng1 = region.longitude - region.longitudeDelta / 2 + const lng2 = region.longitude + region.longitudeDelta / 2 + const minlvl = DEFAULT_ZOOM_TO_LEVEL[Math.min(props.zoom - 1, DEFAULT_ZOOM_TO_LEVEL.length - 1)] + const minlen = DEFAULT_ZOOM_TO_LINK_LENGTH[Math.min(props.zoom - 1, DEFAULT_ZOOM_TO_LINK_LENGTH.length - 1)] / 1000 + const renderPortal = Object + .keys(store.entities.portals) + .filter(guid => store.entities.portals[guid].level >= props.levels[0] && store.entities.portals[guid].level <= props.levels[1]) + .filter(guid => store.entities.portals[guid].coords.latitude > lat1 && store.entities.portals[guid].coords.latitude < lat2 && store.entities.portals[guid].coords.longitude > lng1 && store.entities.portals[guid].coords.longitude < lng2) + + const renderLink = Object + .keys(store.entities.links) + .filter(guid => { + const l1 = store.entities.links[guid].coords[0].latitude + const l2 = store.entities.links[guid].coords[1].latitude + const n1 = store.entities.links[guid].coords[0].longitude + const n2 = store.entities.links[guid].coords[1].longitude + return !(l1 < lat1 && l2 < lat1 || l1 > lat2 && l2 > lat2 || n1 < lng1 && n2 < lng1 || n1 > lng2 && n2 > lng2) && calcCrow(l1, n1, l2, n2) >= minlen + }) + //.map(guid => this.drawLink(guid, links[guid])) + const renderField = Object + .keys(store.entities.fields) + .filter(guid => { + const l1 = store.entities.fields[guid].coords[0].guid + const l2 = store.entities.fields[guid].coords[1].guid + const l3 = store.entities.fields[guid].coords[2].guid + return store.entities.portals[l1] && store.entities.portals[l1].level >= minlvl || + store.entities.portals[l2] && store.entities.portals[l2].level >= minlvl || + store.entities.portals[l3] && store.entities.portals[l3].level >= minlvl + }) + return { + portals: store.entities.portals, + fields: store.entities.fields, + links: store.entities.links, + renderPortal, + renderField, + renderLink + } +}, {})( + MapObjects +);
\ No newline at end of file diff --git a/src/Components/MapOverlay.tsx b/src/Components/MapOverlay.tsx index faf484e..0e3624b 100644 --- a/src/Components/MapOverlay.tsx +++ b/src/Components/MapOverlay.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { StyleSheet, View, Text, GestureResponderEvent, ActivityIndicator, Linking } from 'react-native'; +import { StyleSheet, View, Text, GestureResponderEvent, ActivityIndicator, Linking, TextInput, Slider } from 'react-native'; import { FontAwesome } from '@expo/vector-icons'; import { getStatusBarHeight, getBottomSpace } from '../helper'; import { LatLng } from '../Api/interfaces'; @@ -15,11 +15,6 @@ type Props = { export default (props: Props) => ( <> <View style={styles.overlayLeft}> - {props.loading > 0 ? ( - <View style={styles.loader}> - <ActivityIndicator color={"#000"} /> - </View> - ) : null} {props.selectedPortal ? ( <> <View style={styles.buttonWrapper}> @@ -35,7 +30,7 @@ export default (props: Props) => ( backgroundColor={"#e3e3e3"} /> </View> - <View style={styles.buttonWrapper}> + {/* <View style={styles.buttonWrapper}> <FontAwesome.Button style={styles.button} name={'location-arrow'} @@ -45,9 +40,14 @@ export default (props: Props) => ( size={25} backgroundColor={"#e3e3e3"} /> - </View> + </View> */} </> ) : null} + {props.loading > 0 ? ( + <View style={styles.loader}> + <ActivityIndicator color={"#000"} /> + </View> + ) : null} </View> <View style={styles.overlayRight}> @@ -74,18 +74,23 @@ export default (props: Props) => ( /> </View> </View> + {props.selectedPortal ? ( + <View style={styles.panel}> + <Text>{JSON.stringify(props.selectedPortal)}</Text> + </View> + ) : null} </> ); const styles = StyleSheet.create({ overlayLeft: { position: 'absolute', - bottom: getBottomSpace(), + top: getStatusBarHeight() + 20, left: 8, }, overlayRight: { position: 'absolute', - bottom: getBottomSpace(), + top: getStatusBarHeight() + 20, right: 8, }, button: { @@ -93,9 +98,37 @@ const styles = StyleSheet.create({ alignItems: 'center', height: 48, width: 48, + }, buttonWrapper: { margin: 8, + //ios + shadowOpacity: 0.3, + shadowRadius: 3, + shadowOffset: { + height: 0, + width: 0 + }, + //android + elevation: 1 + }, + panel: { + position: 'absolute', + bottom: getBottomSpace(), + left: 8, + right: 8, + borderRadius: 8, + padding: 8, + backgroundColor: '#fff', + //ios + shadowOpacity: 0.3, + shadowRadius: 3, + shadowOffset: { + height: 0, + width: 0 + }, + //android + elevation: 1 }, icon: { marginRight: 0, diff --git a/src/Components/Settings.tsx b/src/Components/Settings.tsx index a99e46e..ca481a9 100644 --- a/src/Components/Settings.tsx +++ b/src/Components/Settings.tsx @@ -1,5 +1,5 @@ import React, { Component, PureComponent } from 'react'; -import { StyleSheet, View, Text, GestureResponderEvent, ActivityIndicator } from 'react-native'; +import { StyleSheet, View, Text, GestureResponderEvent, ActivityIndicator, Slider, SafeAreaView } from 'react-native'; // import { Button } from 'react-native-vector-icons/FontAwesome'; import { getStatusBarHeight } from '../helper'; import { connect } from 'react-redux'; @@ -20,15 +20,37 @@ class Settings extends PureComponent<Props> { }; render() { + const { filterLevel } = this.props.settings return ( - <View style={styles.overlay}> - {/* <Text>{JSON.stringify(this.props)}</Text> */} - </View> + <SafeAreaView style={styles.overlay}> + <Text>Мин. Ур.: {filterLevel[0]}</Text> + <Slider + style={{ width: 150, height: 40 }} + minimumValue={0} + maximumValue={filterLevel[1]} + value={filterLevel[0]} + step={1} + minimumTrackTintColor="#028ce3" + maximumTrackTintColor="#000000" + onSlidingComplete={this.props.setLevelFrom} + /> + <Text>Макс. Ур.: {filterLevel[1]}</Text> + <Slider + style={{ width: 150, height: 40 }} + minimumValue={filterLevel[0]} + maximumValue={8} + value={filterLevel[1]} + step={1} + minimumTrackTintColor="#028ce3" + maximumTrackTintColor="#000000" + onSlidingComplete={this.props.setLevelTo} + /> + </SafeAreaView> ); } } -export default connect((store) => ({}), (dispatch) => bindActionCreators(actions, dispatch))(Settings) +export default connect((store) => ({ settings: store.settings }), (dispatch) => bindActionCreators(actions, dispatch))(Settings) const styles = StyleSheet.create({ overlay: { diff --git a/src/Store/store.ts b/src/Store/store.ts index b8f4169..581f14b 100644 --- a/src/Store/store.ts +++ b/src/Store/store.ts @@ -19,13 +19,12 @@ const reducers = { 'portalsSet': (store: any, action: { portals: Portal }) => { const portals = store.portals Object.keys(action.portals).forEach(guid => { - const portal: Portal = portals[guid] - const newPortal: Portal = action.portals[guid] - if (!portal) { - portals[guid] = newPortal - return + if (portals[guid]) { + portals[guid] = { ...portals[guid], fraction: action.portals[guid].fraction, level: action.portals[guid].level } + } else { + portals[guid] = action.portals[guid] } - portals[guid] = extend(portal, newPortal) + }) return { ...store, portals } }, @@ -35,8 +34,43 @@ const reducers = { ({ ...store, fields: { ...store.fields, ...action.fields } }), 'setLoad': (store: any, action: { queue: string[][] }) => ({ ...store, loadQueue: [...action.queue] }), - - }, { portals: {}, fields: {}, links: {}, loadQueue: [] }) + 'linksGC': (store: any, action: { links: { [tile: string]: string[] } }) => { + let linksCache = store.linksCache + let links = store.links + Object.keys(action.links).forEach(tileId => { + const oldLinks = linksCache[tileId] + if (oldLinks != undefined) { + oldLinks.forEach((guid: string) => { + delete links[guid] + }) + } + linksCache[tileId] = action.links[tileId] + }) + return ({ ...store, links: { ...links }, linksCache }) + }, + 'fieldsGC': (store: any, action: { fields: { [tile: string]: string[] } }) => { + let fieldsCache = store.fieldsCache + let fields = store.fields + Object.keys(action.fields).forEach(tileId => { + const oldFields = fieldsCache[tileId] + if (oldFields != undefined) { + oldFields.forEach((guid: string) => { + delete fields[guid] + }) + } + fieldsCache[tileId] = action.fields[tileId] + }) + return ({ ...store, fields: { ...fields }, fieldsCache }) + }, + }, { portals: {}, fields: {}, links: {}, loadQueue: [], linksCache: {}, fieldsCache: {} }), + 'settings': createReducer({ + 'setLevelFrom': (store: any, action: { level: number }) => ({ + ...store, filterLevel: [action.level, store.filterLevel[1]] + }), + 'setLevelTo': (store: any, action: { level: number }) => ({ + ...store, filterLevel: [store.filterLevel[0], action.level] + }) + }, { filterLevel: [0, 8] }) } function extend(obj: any, src: any) { diff --git a/src/constants.ts b/src/constants.ts index e24771a..288c907 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -6,6 +6,10 @@ export const COLORS_MOD = { VERY_RARE: '#b08cff', RARE: '#73a8ff', COMMON: '#8cf export const RESO_NRG = [0, 1000, 1500, 2000, 2500, 3000, 4000, 5000, 6000]; export const MOD_TYPE = { RES_SHIELD: 'Shield', MULTIHACK: 'Multi-hack', FORCE_AMP: 'Force Amp', HEATSINK: 'Heat Sink', TURRET: 'Turret', LINK_AMPLIFIER: 'Link Amp' }; +export const DEFAULT_ZOOM_TO_LEVEL = [8,8,8,8,7,7,7,6,6,5,4,4,3,2,2,1,1,1,1,1,1,1,1]; +export const DEFAULT_ZOOM_TO_LINK_LENGTH = [200000,200000,200000,200000,200000,60000,60000,10000,5000,2500,2500,800,300,0,0]; + + export const NAVIGATORS = { "ymaps": "yandexnavi://build_route_on_map?lat_to=<lat>&lon_to=<lon>&no-balloon=0&desc=<title>", "2gis": "dgis://2gis.ru/routeSearch/rsType/car/to/<lon>,<lat>", diff --git a/src/helper.js b/src/helper.js index 3bdbf7e..2242d91 100644 --- a/src/helper.js +++ b/src/helper.js @@ -43,4 +43,25 @@ export function timeConverter(UNIX_timestamp) { function formatInt(num) { return ("00" + num).slice(-2) -}
\ No newline at end of file +} + +export function calcCrow(lat1, lon1, lat2, lon2) + { + var R = 6371; // km + var dLat = toRad(lat2-lat1); + var dLon = toRad(lon2-lon1); + var lat1 = toRad(lat1); + var lat2 = toRad(lat2); + + var a = Math.sin(dLat/2) * Math.sin(dLat/2) + + Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2); + var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); + var d = R * c; + return d; + } + + // Converts numeric degrees to radians + function toRad(Value) + { + return Value * Math.PI / 180; + }
\ No newline at end of file |