summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlexander NeonXP Kiryukhin <a.kiryukhin@mail.ru>2019-06-10 03:15:51 +0300
committerAlexander NeonXP Kiryukhin <a.kiryukhin@mail.ru>2019-06-10 03:15:51 +0300
commit06645cdac4184f5c1cf50f2a4b94be3d72d634f0 (patch)
tree1eb62ec1d2275ae6feb90cfceedb7b93257005b0 /src
parent4b01d81d3daed894bc93f77dbbbe5501a4552447 (diff)
More improvements
Diffstat (limited to 'src')
-rw-r--r--src/Actions/actions.ts2
-rw-r--r--src/Actions/entity.ts13
-rw-r--r--src/Actions/settings.ts11
-rw-r--r--src/Api/api.ts8
-rw-r--r--src/Components/Map.tsx29
-rw-r--r--src/Components/MapObjects.tsx128
-rw-r--r--src/Components/MapOverlay.tsx53
-rw-r--r--src/Components/Settings.tsx32
-rw-r--r--src/Store/store.ts50
-rw-r--r--src/constants.ts4
-rw-r--r--src/helper.js23
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