Skip to content

Commit

Permalink
Merge pull request #2707 from serge-web/2706_bulk_data
Browse files Browse the repository at this point in the history
2706 bulk data
  • Loading branch information
IanMayo authored Nov 29, 2023
2 parents 57d2213 + 6cbb2af commit 7ee05ea
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import L, { LatLngBounds } from 'leaflet'
import { Feature, FeatureCollection, Geometry } from 'geojson'
import { RENDERER_CORE, RENDERER_MILSYM } from 'src/custom-types'
import { Phase } from 'src/config'

const genGeometry = (bounds: LatLngBounds): Geometry => {
const lat1 = bounds.getSouth()
const lat2 = bounds.getNorth()
const lng1 = bounds.getWest()
const lng2 = bounds.getEast()
if (Math.random() > 0.3) {
return {
type: 'Polygon',
coordinates: [[[lng1, lat1], [lng1, lat2], [lng2, lat2], [lng2, lat1], [lng1, lat1]]]
}
} else {
return {
type: 'LineString',
coordinates: [[lng1, lat1], [lng1, lat2], [lng2, lat1], [lng2, lat2]]
}
}
}

export const generateFeatures = (bounds: L.LatLngBounds, numPoints: number, numShapes: number): FeatureCollection => {
const features: Feature[] = []
const x_min = bounds.getEast()
const x_max = bounds.getWest()
const y_min = bounds.getSouth()
const y_max = bounds.getNorth()
const randString = (items: string[]): string => {
return items[Math.floor(Math.random() * items.length)]
}
const forces = ['f-red', 'f-blue', 'f-green']
const sizes = ['S', 'M', 'L']
const sidcValues = ['SUP*------*****', 'SFP*------*****', 'SNP*------*****', 'SHP*------*****', 'SUP*S-----*****', 'SFP*S-----*****', 'SNP*S-----*****', 'SHP*S-----*****', 'SUP*V-----*****', 'SFP*V-----*****', 'SNP*V-----*****', 'SHP*V-----*****', 'SUP*T-----*****', 'SFP*T-----*****', 'SNP*T-----*****', 'SHP*T-----*****', 'SUP*L-----*****', 'SFP*L-----*****', 'SNP*L-----*****', 'SHP*L-----*****', 'SUA*------*****', 'SFA*------*****', 'SNA*------*****', 'SHA*------*****', 'SUA*M-----*****', 'SFA*M-----*****', 'SNA*M-----*****', 'SHA*M-----*****', 'SUA*MF----*****', 'SFA*MF----*****', 'SNA*MF----*****', 'SHA*MF----*****', 'SUA*MFB---*****', 'SFA*MFB---*****', 'SHA*MFP---*****', 'SUA*MFPN--*****', 'SFA*MFPN--*****', 'SNA*MFPN--*****', 'SHA*MFPN--*****', 'SUA*MFPM--*****', 'SFA*MFPM--*****', 'SNA*MFPM--*****', 'SHA*MFPM--*****', 'SUA*MFU---*****', 'SFA*MFU---*****', 'SNA*MFU---*****', 'SHA*MFU---*****', 'SUA*MFUL--*****', 'SFA*MFUL--*****', 'SNA*MFUL--*****', 'SHA*MFUL--*****', 'SUA*MFUM--*****', 'SFA*MFUM--*****', 'SNA*MFUM--*****', 'SHA*MFUM--*****', 'SUA*MFUH--*****', 'SFA*MFUH--*****', 'SNA*MFUH--*****', 'SHA*MFUH--*****', 'SUA*MFY---*****', 'SFA*MFY---*****', 'SNA*MFY---*****', 'SHA*MFY---*****', 'SUA*MFH---*****', 'SFA*MFH---*****', 'SNA*MFH---*****', 'SHA*MFH---*****', 'SUA*MFD---*****', 'SFA*MFD---*****', 'SNA*MFD---*****', 'SHA*MFD---*****', 'SUA*MFQ---*****', 'SFA*MFQ---*****', 'SNA*MFQ---*****', 'SHA*MFQ---*****', 'SUA*MFQA--*****', 'SNA*MFQP--*****', 'SHA*MFQP--*****', 'SUA*MFQR--*****', 'SFA*MFQR--*****', 'SNA*MFQR--*****', 'SHA*MFQR--*****', 'SUA*MFQRW-*****', 'SFA*MFQRW-*****', 'SNA*MFQRW-*****', 'SHA*MFQRW-*****', 'SUA*MFQRZ-*****', 'SFA*MFQRZ-*****', 'SNA*MFQRZ-*****', 'SHA*MFQRZ-*****', 'SUA*MFQRX-*****', 'SFA*MFQRX-*****', 'SNA*MFQRX-*****', 'SHA*MFQRX-*****', 'SUA*MFQS--*****', 'SFA*MFQS--*****', 'SNA*MFQS--*****', 'SHA*MFQS--*****', 'SUA*MFQT--*****', 'SFA*MFQT--*****', 'SNA*MFQT--*****', 'SHA*MFQT--*****', 'SUA*MFQU--*****', 'SFA*MFQU--*****', 'SUA*MHUH--*****', 'SFA*MHUH--*****', 'SNA*MHUH--*****', 'SHA*MHUH--*****', 'SUA*MHI---*****', 'SFA*MHI---*****', 'SNA*MHI---*****', 'SHA*MHI---*****', 'SUA*MHH---*****', 'SFA*MHH---*****', 'SNA*MHH---*****', 'SHA*MHH---*****', 'SUA*MHR---*****', 'SFA*MHR---*****', 'SNA*MHR---*****', 'SHA*MHR---*****', 'SUA*MHQ---*****', 'SFA*MHQ---*****', 'SNA*MHQ---*****', 'SHA*MHQ---*****', 'SUA*MHC---*****', 'SFG*UCAWR-*****', 'SNG*UCAWR-*****', 'SHG*UCAWR-*****', 'SUG*UCAA--*****', 'SFG*UCAA--*****', 'SNG*UCAA--*****', 'SHG*UCAA--*****', 'SUG*UCAAD-*****', 'SFG*UCAAD-*****', 'SNG*UCAAD-*****', 'SHG*UCAAD-*****', 'SUG*UCAAL-*****', 'SFG*UCAAL-*****', 'SNG*UCAAL-*****', 'SFG*UCVRS-*****', 'SNG*UCVRS-*****', 'SHG*UCVRS-*****', 'SUG*UCVRW-*****', 'SFG*UCVRW-*****', 'SNG*UCVRW-*****', 'SHG*UCVRW-*****', 'SUG*UCVRU-*****', 'SFG*UCVRU-*****', 'SNG*UCVRU-*****', 'SHG*UCVRU-*****', 'SUG*UCVRUL*****', 'SFG*UCVRUL*****', 'SNG*UCVRUL*****', 'SHG*UCVRUL*****', 'SUG*UCVRUM*****', 'SFG*UCVRUM*****', 'SNG*UCVRUM*****', 'SHG*UCVRUM*****', 'SUG*UCVRUH*****', 'SFG*UCVRUH*****', 'SNG*UCVRUH*****', 'SHG*UCVRUH*****', 'SUG*UCVRUC*****', 'SFG*UCVRUC*****', 'SNG*UCVRUC*****', 'SHG*UCVRUC*****', 'SUG*UCVRUE*****', 'SFG*UCVRUE*****', 'SNG*UCVRUE*****', 'SHG*UCVRUE*****', 'SUG*UCVRM-*****', 'SFG*UCVRM-*****', 'SNG*UCVRM-*****', 'SHG*UCVRM-*****', 'SUG*UCVS--*****', 'SFG*UCVS--*****', 'SNG*UCVS--*****', 'SHG*UCVS--*****', 'SUG*UCVC--*****', 'SFG*UCVC--*****', 'SNG*UCVC--*****', 'SHG*UCVC--*****', 'SUG*UCVV--*****', 'SFG*UCVV--*****', 'SNG*UCVV--*****', 'SHG*UCVV--*****', 'SUG*UCVU--*****', 'SFG*UCVU--*****', 'SNG*UCVU--*****', 'SHG*UCVU--*****', 'SUG*UCVUF-*****', 'SFG*UCVUF-*****', 'SNG*UCVUF-*****', 'SHG*UCVUF-*****', 'SUG*UCVUR-*****', 'SFG*UCVUR-*****', 'SNG*UCVUR-*****', 'SHG*UCVUR-*****', 'SUG*UCI---*****', 'SFG*UCI---*****', 'SNG*UCI---*****', 'SHG*UCI---*****', 'SUG*UCIL--*****', 'SFG*UCIL--*****', 'SNG*UCIL--*****', 'SHG*UCIL--*****', 'SUG*UCIM--*****', 'SFG*UCIM--*****', 'SNG*UCIM--*****', 'SFG*UCECA-*****', 'SNG*UCECA-*****', 'SHG*UCECA-*****', 'SUG*UCECC-*****', 'SFG*UCECC-*****', 'SNG*UCECC-*****', 'SHG*UCECC-*****', 'SUG*UCECL-*****', 'SFG*UCECL-*****', 'SNG*UCECL-*****', 'SHG*UCECL-*****', 'SUG*UCECM-*****', 'SFG*UCECM-*****', 'SNG*UCECM-*****', 'SHG*UCECM-*****', 'SUG*UCECH-*****', 'SFG*UCECH-*****', 'SNG*UCECH-*****', 'SHG*UCECH-*****', 'SUG*UCECT-*****', 'SFG*UCECT-*****', 'SNG*UCECT-*****', 'SHG*UCECT-*****', 'SUG*UCECW-*****', 'SFG*UCECW-*****', 'SNG*UCECW-*****', 'SHG*UCECW-*****', 'SUG*UCECO-*****', 'SUG*UCFMTS*****', 'SFG*UCFMTS*****', 'SNG*UCFMTS*****', 'SHG*UCFMTS*****', 'SUG*UCFMTC*****', 'SFG*UCFMTC*****', 'SNG*UCFMTC*****', 'SHG*UCFMTC*****', 'SUG*UCFMTO*****', 'SFG*UCFMTO*****', 'SNG*UCFMTO*****', 'SHG*UCFMTO*****', 'SUG*UCFML-*****', 'SFG*UCFML-*****', 'SNG*UCFML-*****', 'SHG*UCFML-*****', 'SUG*UCFS--*****', 'SFG*UCFS--*****', 'SNG*UCFS--*****', 'SHG*UCFS--*****', 'SUG*UCFSS-*****', 'SFG*UCFSS-*****', 'SNG*UCFSS-*****', 'SHG*UCFSS-*****', 'SUG*UCFSA-*****', 'SFG*UCFSA-*****', 'SNG*UCFSA-*****', 'SHG*UCFSA-*****', 'SUG*UCFSL-*****', 'SFG*UCFSL-*****', 'SNG*UCFSL-*****', 'SHG*UCFSL-*****', 'SUG*UCFSO-*****', 'SFG*UCFSO-*****', 'SNG*UCFSO-*****', 'SHG*UCFSO-*****', 'SUG*UCFO--*****', 'SFG*UCFO--*****', 'SNG*UCFO--*****', 'SHG*UCFO--*****', 'SUG*UCFOS-*****', 'SFG*UCFOS-*****', 'SNG*UCFOS-*****', 'SHG*UCFOS-*****', 'SUG*UCFOA-*****', 'SFG*UCFOA-*****', 'SNG*UCFOA-*****', 'SHG*UCFOA-*****', 'SUG*UCFOL-*****', 'SFG*UCFOL-*****', 'SNG*UCFOL-*****', 'SHG*UCFOL-*****', 'SUG*UCFOO-*****', 'SFG*UCFOO-*****', 'SNG*UCFOO-*****', 'SHG*UCFOO-*****', 'SUG*UCR---*****', 'SFG*UCR---*****', 'SNG*UCR---*****', 'SHG*UCR---*****', 'SUG*UCRH--*****', 'SFG*UCRH--*****', 'SNG*UCRH--*****', 'SHG*UCRH--*****', 'SUG*UCRV--*****', 'SFG*UCRV--*****', 'SNG*UCRV--*****', 'SHG*UCRV--*****', 'SUG*UCRVA-*****', 'SFG*UCRVA-*****', 'SUF*NS----*****', 'SFF*NS----*****', 'SNF*NS----*****', 'SHF*NS----*****', 'SUF*NU----*****', 'SFF*NU----*****', 'SNF*NU----*****', 'SHF*NU----*****', 'SUF*NB----*****', 'SFF*NB----*****', 'SNF*NB----*****', 'SHF*NB----*****', 'SUF*NN----*****', 'SFF*NN----*****', 'SNF*NN----*****', 'SHF*NN----*****', 'SUF*G-----*****', 'SFF*G-----*****', 'SNF*G-----*****', 'SHF*G-----*****', 'SUF*GS----*****', 'SFF*GS----*****', 'SNF*GS----*****', 'SHF*GS----*****', 'SUF*GR----*****', 'SFF*GR----*****', 'SNF*GR----*****', 'SHF*GR----*****', 'SUF*GP----*****', 'SFF*GP----*****', 'SNF*GP----*****', 'SHF*GP----*****', 'SUF*GPA---*****', 'SFF*GPA---*****', 'SNF*GPA---*****', 'SHF*GPA---*****', 'SUF*GC----*****', 'SFF*GC----*****', 'SNF*GC----*****', 'SHF*GC----*****', 'SUF*B-----*****', 'SFF*B-----*****', 'SNF*B-----*****', 'SHF*B-----*****']

for (let i = 0; i < numPoints; i++) {
const lat = y_min + (Math.random() * (y_max - y_min))
const lng = x_min + (Math.random() * (x_max - x_min))
const newF: Feature = {
type: 'Feature',
properties: {
id: 'pt_' + i,
force: randString(forces),
phase: randString([Phase.Adjudication, Phase.Planning]),
turn: Math.floor(Math.random() * 5),
_type: RENDERER_MILSYM,
sidc: randString(sidcValues), // 'SFG-UCI----D',
category: 'Civilian',
size: randString(sizes)
},
geometry: {
type: 'Point',
coordinates: [lng, lat]
}
}
features.push(newF)
}
for (let i = 0; i < numShapes; i++) {
const size = Math.random() * 2.6
const lat1 = y_min + (Math.random() * (y_max - y_min))
// note: 1.6 scaling factor in next line is to reflect latitude of sample data,
// so boxes are still roughly square
const lng1 = x_min + (Math.random() * (x_max - x_min)) * 1.6
const lat2 = lat1 + Math.random() * size
const lng2 = lng1 + Math.random() * size
const bounds = L.latLngBounds(L.latLng(lat1, lng1), L.latLng(lat2, lng2))
const geometry = genGeometry(bounds)
const newF: Feature = {
type: 'Feature',
properties: {
id: 'pt_' + i,
force: randString(forces),
phase: randString([Phase.Adjudication, Phase.Planning]),
turn: Math.floor(Math.random() * 5),
_type: RENDERER_CORE,
important: 'Yes'
},
geometry
}
features.push(newF)
}
const collection: FeatureCollection = {
type: 'FeatureCollection',
features: features
}
return collection
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* global it expect */
import React from 'react'
import L from 'leaflet'
import renderer from 'react-test-renderer'
import CoreMapping from './index'
import { Phase } from 'src/config'
Expand All @@ -26,10 +27,12 @@ const channel: CoreMappingChannel = {
}
}

const bounds = L.latLngBounds(L.latLng(51.405, -0.02), L.latLng(51.605, -0.13))

describe('Core Mapping component:', () => {
it('renders correctly', () => {
const tree = renderer
.create(<CoreMapping playerForce={'f-red'} messages={[]} channel={channel} playerRole={'mgr'} currentTurn={1} forces={[]} currentPhase={Phase.Planning}/>)
.create(<CoreMapping bounds={bounds} playerForce={'f-red'} messages={[]} channel={channel} playerRole={'mgr'} currentTurn={1} forces={[]} currentPhase={Phase.Planning}/>)
.toJSON()
expect(tree).toMatchSnapshot()
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { withKnobs } from '@storybook/addon-knobs'
import React from 'react'
import L from 'leaflet'
import CoreMapping from './index'
import docs from './README.md'
import { Phase } from 'src/config'
import { CHANNEL_CORE_MAPPING, CORE_MAPPING, CoreMappingChannel, CoreMappingMessage, CoreProperties, CoreRenderer, EnumProperty, MilSymProperties, MilSymRenderer, NumberProperty, PARTICIPANT_CORE_MAPPING, RENDERER_CORE, RENDERER_MILSYM } from 'src/custom-types'
import { Feature, FeatureCollection } from 'geojson'
import { generateFeatures } from './helper/feature-generator'

const wrapper: React.FC = (storyFn: any) => <div style={{ height: '600px', position: 'relative' }}>{storyFn()}</div>

Expand All @@ -20,6 +22,9 @@ export default {
}
}

const largeBounds = L.latLngBounds(L.latLng(45, -30), L.latLng(60, 30))
const bounds = L.latLngBounds(L.latLng(51.405, -0.02), L.latLng(51.605, -0.13))

/** PROPERTY DEFINITIONS */

const importantProp: EnumProperty = {
Expand Down Expand Up @@ -239,8 +244,33 @@ const coreMapChannel: CoreMappingChannel = {
renderers: [coreRenderer, milSymRenderer]
}

const bulkMessage: CoreMappingMessage = {
_id: 'timestamp-23',
details: {
channel: 'core-mapping',
from: {
force: 'f-red',
forceColor: '#f00',
roleId: 'mar-23',
roleName: 'MARITIME CTRL',
iconURL: 'f-red.svg'
},
messageType: 'custom',
timestamp: '2023-11-23T23:32:00',
turnNumber: 1
},
messageType: CORE_MAPPING,
features: generateFeatures(largeBounds, 300, 30)
}

export const Default: React.FC = () => {
return (
<CoreMapping playerForce={'f-red'} messages={[coreMessage]} channel={coreMapChannel} playerRole={'mgr'} currentTurn={1} forces={[]} currentPhase={Phase.Planning}/>
<CoreMapping bounds={bounds} playerForce={'f-red'} messages={[coreMessage]} channel={coreMapChannel} playerRole={'mgr'} currentTurn={1} forces={[]} currentPhase={Phase.Planning}/>
)
}

export const Bulk: React.FC = () => {
return (
<CoreMapping bounds={largeBounds} playerForce={'f-red'} messages={[bulkMessage]} channel={coreMapChannel} playerRole={'mgr'} currentTurn={1} forces={[]} currentPhase={Phase.Planning}/>
)
}
8 changes: 3 additions & 5 deletions client/src/Components/local/atoms/core-mapping/index.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
import { FeatureCollection } from 'geojson'
import { LatLngExpression, PM } from 'leaflet'
import React, { useEffect, useState } from 'react'
import { LayerGroup, MapContainer, TileLayer } from 'react-leaflet-v4'
import { BaseRenderer, CoreMappingMessage } from 'src/custom-types'
import { CoreRendererHelper } from './helper/core-renderer-helper'
import MapControls from './helper/map-controls'
import { PM } from 'leaflet'
import { loadDefaultMarker } from './helper/marker-helper'
import styles from './styles.module.scss'
import PropTypes, { CoreRendererProps } from './types/props'

const CoreMapping: React.FC<PropTypes> = ({ messages, channel }) => {
const CoreMapping: React.FC<PropTypes> = ({ messages, channel, bounds }) => {
const [features, setFeatures] = useState<FeatureCollection>()
const [renderers, setRenderers] = useState<React.ComponentClass<CoreRendererProps>[]>([])

const position: LatLngExpression = [51.505, -0.09]

useEffect(() => {
loadDefaultMarker()
}, [])
Expand Down Expand Up @@ -46,7 +44,7 @@ const CoreMapping: React.FC<PropTypes> = ({ messages, channel }) => {
console.log('onChange Event Fired', e)
}

return <MapContainer center={position} zoom={13} scrollWheelZoom={false} className={styles.container}>
return <MapContainer bounds={bounds} zoom={13} scrollWheelZoom={false} className={styles.container}>
<TileLayer
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,42 @@

import React from 'react'
import { GeoJSON } from 'react-leaflet-v4'
import { RENDERER_CORE } from 'src/custom-types'
import { CoreProperties, RENDERER_CORE } from 'src/custom-types'
import { CoreRendererProps } from '../types/props'
import { Feature, Geometry } from 'geojson'
import { PathOptions, StyleFunction } from 'leaflet'

const colorFor = (force: string) => {
switch (force) {
case 'f-red':
return '#F00'
case 'f-blue':
return '#00F'
case 'f-green':
return '#0F0'
default:
return '#aaa'
}
}

export class CoreRenderer extends React.Component<CoreRendererProps> {
render (): React.ReactElement {
const filter = (feature: Feature<Geometry, any>): boolean => feature.properties._type === RENDERER_CORE
return <GeoJSON data={this.props.features} filter={filter} key={'feature_no_contact' + Math.random()}/>
const style: StyleFunction<any> = (feature?: Feature<any>): PathOptions => {
if (feature) {
const props = feature.properties as CoreProperties
const color: string = colorFor(props.force)
const weight = feature.geometry.type === 'Polygon' ? 1 : 3
return {
color,
weight,
fillColor: color,
className: 'leaflet-default-icon-path'
}
} else {
return {}
}
}
return <GeoJSON data={this.props.features} style={style} filter={filter} key={'feature_no_contact' + Math.random()}/>
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export default interface PropTypes {
forces: ForceStyle[]
channel: CoreMappingChannel
messages: CoreMappingMessage[]
bounds: L.LatLngBounds
}

export type CoreRendererProps = {
Expand Down

0 comments on commit 7ee05ea

Please sign in to comment.