UNPKG

agentscript

Version:

AgentScript Model in Model/View architecture

306 lines (267 loc) 11.6 kB
<html> <head> <title>Droplets</title> <link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css"> <link rel="stylesheet" href="./map.css"> </head> <body> <div id="map"></div> <script type="module"> import * as util from '../src/utils.js' import * as gis from '../src/gis.js' import * as TileData from '../src/TileData.js' import * as leafletUtils from './leafletUtils.js' import BBoxDataSet from '../src/BBoxDataSet.js' import TwoDraw from '../src/TwoDraw.js' import Animator from '../src/Animator.js' import GUI from '../src/GUI.js' import Model from '../models/DropletsModel.js' import * as L from 'https://unpkg.com/leaflet@1.8.0/dist/leaflet-src.esm.js' import elementOverlay from '../vendor/elementOverlay.js' // await util.fetchCssStyle('https://unpkg.com/leaflet/dist/leaflet.css') // await util.fetchCssStyle('./map.css') const ElementOverlay = elementOverlay(L) const tileData = TileData['mapzen'] // redfishUSA/World, mapzen, mapbox const terrainName = 'topo' // ================ create map with terrain & elevation ================ const center = [35.67, -105.93] const Z = 10 // 10 11 const map = L.map('map', { zoomDelta: 0.25, //0.1 zoomSnap: 0, preferCanvas: true, // renderer: L.canvas() }).setView(center, Z) const terrainLayer = L.tileLayer(gis.template(terrainName), { attribution: gis.attribution(terrainName), // className: 'terrain-pane' // shows tiles borders }).addTo(map) const elevationLayer = L.tileLayer( tileData.zxyTemplate, { opacity: 0.3, attribution: 'Elevation Tiles', crossOrigin: '', maxZoom: tileData.maxZoom, // larger mapzen zoom yields tileerror } ).addTo(map) // util.toWindow({ terrainLayer, elevationLayer, map }) // ================ create dataset from bbox tiles ================ // let leafletDataSet = new LeafletDataSet( // map, // elevationLayer, // // tileData // ) // leafletUtils.testEvents(map, elevationLayer) // leafletUtils.testZooms(elevationLayer) // ================ create model using bbox dataset ================ let modelLayer, model, view, anim //, drawOptions async function setupModelView(bounds = map.getBounds()) { let bbox = gis.Lbounds2bbox(bounds) let zoom = Math.round(map.getZoom()) let bboxDataSet = await new BBoxDataSet() // default tileData, tileSize .getBBoxDataSet(bbox, zoom) // const bboxDataSet = await leafletDataSet.getBBoxDataSet(bounds) const bboxExtent = bboxDataSet.extent() // util.toWindow({ leafletDataSet, bboxDataSet, bboxExtent, bounds }) // console.log('leafletDataSet', leafletDataSet, 'bboxDataSet', bboxDataSet) const patchSize = 4 const worldOptions = { minX: 0, minY: 0, maxX: Math.round(bboxDataSet.width / patchSize) - 1, maxY: Math.round(bboxDataSet.height / patchSize) - 1, } if (model) model.reset(worldOptions) else model = new Model(worldOptions) // await model.startup([Z, X, Y]) // TEST await model.startup(bboxDataSet) model.setup() model.elementBounds = bounds model.bboxDataSet = bboxDataSet // drawOptions = drawOptions || { const drawOptions = view?.drawOptions || { patchesMap: 'Jet', // 'transparent', initPatches: (model, view) => { const colorMap = view.drawOptions.patchesMap const colors = model.patches.map( p => p.isLocalMin ? 'black' : colorMap.scaleColor(p.elevation, ...bboxExtent)) return colors }, turtlesShape: 'square', turtlesRotate: false, turtlesColor: 'rgb(255,0,0)', // 'red', turtlesSize: 0.8, } // view = view || new TwoDraw(model, { view = new TwoDraw(model, { div: util.createCanvas(0, 0), // the view will resize patchSize: patchSize, useSprites: true, drawOptions: drawOptions, // will be view.drawOptions if available }) // drawOptions = view.drawOptions // Note: when zooming happens after adding the canvas, remove the current one // and draw it on a new larger canvas with ctx.imageSmoothingEnabled = false if (modelLayer) modelLayer.remove() view.canvas.style.border = '1px red solid' modelLayer = new ElementOverlay(view.canvas, bounds).addTo(map) // const modelLayer = new ElementOverlay(view.canvas, gis.bbox2bounds(bbox)).addTo(map) if (!anim) { anim = new Animator( () => { model.step() view.draw() // gui.update() if (model.moves === 0) { anim.stop() console.log('No moves, stopping at step', model.ticks) } }, -1, //500,// -1, // 500, // run 500 steps 30 // 10 // 30 // 30 fps ).startStats('70px') // avoid +/= leaflet control anim.setIdle(() => view.draw()) } else { anim.reset() } util.toWindow({ model, view, modelLayer, anim, drawOptions }) } await setupModelView() // default to full page // await setupModelView(gis.santaFeBBox) // test // ================ mouse drag bbox rect ================ const mapMouseDownFcn = (ev) => { if (ev.originalEvent.altKey) { leafletUtils.mouseBounds(L, map, ev, async (bounds) => { await setupModelView(bounds) }) } } map.on('mousedown', mapMouseDownFcn) const mapKeyFcn = async (ev) => { // console.log('key', ev.originalEvent) if (ev.originalEvent.key === 'm') { await setupModelView() } } map.on('keypress', mapKeyFcn) // ================ gui ================ const gui = new GUI({ restartMap: { button: async () => { const atEdge = model.turtles.getDefault('atEdge') await setupModelView() gui.controllers.pause.name('pause') model.turtles.setDefault('atEdge', atEdge) } }, pause: { button: () => { const isRunning = anim.isRunning() gui.controllers.pause.name(isRunning ? 'resume' : 'pause') isRunning ? anim.stop() : anim.start() }, }, stepType: { chooser: [model.stepType, ['minNeighbor', 'patchAspect', 'dataSetAspectNearest', 'dataSetAspectBilinear', ]], cmd: val => (model.stepType = val), }, patchesColors: { // val: ['Jet', ['transparent', 'Jet', 'LightGray']], chooser: ['Transparent', ['Transparent', 'Jet', 'Gray', 'Hue', 'Basic16']], cmd: val => { view.drawOptions.patchesMap = val view.resetOptions(view.drawOptions) // drawOptions = view.resetOptions(drawOptions) if (!anim.isRunning()) view.draw() }, }, 'Opacity': { // puddleDepth: { // val: [model.puddleDepth, [1, 15, 1]], // cmd: async val => { // model.puddleDepth = val // } // }, tilesOpacity: { chooser: [elevationLayer.options.opacity, [0, 1, 0.1]], cmd: val => elevationLayer.setOpacity(val), }, modelOpacity: { slider: [1, [0, 1, 0.1]], cmd: val => view.canvas.style.opacity = val, }, }, 'Model Data': { modelTicks: { monitor: [model, 'ticks'] }, patches: { monitor: [model.patches, 'length'] // cmd: () => model.patches.length, }, turtlesSize: { slider: [0.8, [.2, 1, 0.1]], cmd: val => { view.drawOptions.turtlesSize = val if (!anim.isRunning()) view.draw() }, }, turtlesSpeed: { slider: [model.speed, [0.2, 1, 0.1]], cmd: val => model.speed = val, }, turtlesColor: { color: view.drawOptions.turtlesColor, cmd: val => { view.drawOptions.turtlesColor = val if (!anim.isRunning()) view.draw() }, }, moves: { monitor: [model, 'moves'], // cmd: () => model.moves }, atEdge: { chooser: [model.turtles.getDefault('atEdge'), ['wrap', 'die', 'clamp', 'bounce', 'random']], cmd: val => { model.turtles.setDefault('atEdge', val) }, }, }, 'Downloads': { getElevations: { button: () => util.downloadBlob(model.bboxDataSet, 'elevations.json', false), }, patchesAspect: { button: () => { const dataSet = model.patches.exportDataSet('aspect') dataSet.bounds = model.elementBounds // add metadata util.downloadBlob(dataSet, 'patchesAspect.json', false) } }, turtlesXYHead: { button: () => { const data = model.turtles.map(t => { let { x, y, heading } = t const obj = { x, y, heading } util.forLoop(obj, (val, key) => obj[key] = Math.round(val)) return obj }) util.downloadBlob(data, 'turtlesXYHead.json', false) } }, } }) // util.forLoop(gui.folders, (val) => val.open()) // gui.folders.mapFolder.open() // gui.baseGui.close() // util.toWindow({ GUI: gui.GUI, gui }) </script> </body> </html>