Skip to main content
  • Home
  • Development
  • Documentation
  • Donate
  • Operational login
  • Browse the archive

swh logo
SoftwareHeritage
Software
Heritage
Archive
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

Revision 00e4dbd184a8eecfc8921431fcd1ecc2f966177e authored by Sanmisan on 13 October 2023, 20:18:23 UTC, committed by GitHub on 13 October 2023, 20:18:23 UTC
Update README.md
teaser img
1 parent b49b4e1
  • Files
  • Changes
  • ba30466
  • /
  • src
  • /
  • App.js
Raw File Download

To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
Select below a type of object currently browsed in order to display its associated SWHID and permalink.

  • revision
  • directory
  • content
revision badge
swh:1:rev:00e4dbd184a8eecfc8921431fcd1ecc2f966177e
directory badge
swh:1:dir:cf84a4be4a92807464533ba7fde0714acf906d65
content badge
swh:1:cnt:663f588bee02eb14282ee9f3e1d2f818793aa335

This interface enables to generate software citations, provided that the root directory of browsed objects contains a citation.cff or codemeta.json file.
Select below a type of object currently browsed in order to generate citations for them.

  • revision
  • directory
  • content
(requires biblatex-software package)
Generating citation ...
(requires biblatex-software package)
Generating citation ...
(requires biblatex-software package)
Generating citation ...
App.js
import {Component} from 'react';
import './styles/App.css';
import {Layout} from 'antd';
import {Row, Col, Card, Divider} from 'antd';
import * as turf from "@turf/turf";
//import {Select, Slider, Switch, Empty} from 'antd';
/** import local components */
import NavBar from './components/nav_bar';
import CodeEditor from './components/codeEditor';
import StatusBar from './components/statusBar';
import MapLinter from './components/mapLinter';
import LinterReport from './components/linterReport';
//import LinterCharts from './components/linterCharts';
import SupportMapView from './components/supportMapView';
import ClassRecommend from './components/classRecommend';

/** Import test data, can be uploaded by users */
// import case 1 data -> state-level education statics
import state_education from './resource/case1_state_edu/state_education.json';
import state_education_features from './resource/case1_state_edu/state_education_features.json';

// import case 2 data -> county-level unemployment statics
//import us10m from './resource/us10m.json';
//import unemployment from './resource/unemployment.json';
//import county_unemployment from './resource/case2_county/county_unemployment.json';
//import county_unemployment_features from './resource/case2_county/county_unemployment_features.json';

// import case 3 data -> montreal population density statics
import montreal_density from './resource/case3_montreal/montreal_density.json';
import montreal_density_features from './resource/case3_montreal/montreal_density_features.json';

// import case 4 data -> georgia pctBach statics
import georgia_pctBach from './resource/case4_georgia/georgia_pctBach.json';
import georgia_pctBach_features from './resource/case4_georgia/georgia_pctBach_features.json';

// import case 5 data -> chicago income_pc statics
import chicago_income from './resource/case5_chicago/chicago_income.json';
import chicago_income_features from './resource/case5_chicago/chicago_income_features.json';

// tvcg case 1 -> state_freight_shipments
import state_shipment from './resource/tvcg_c1/state_shipment.json';
import state_shipment_features from './resource/tvcg_c1/state_shipment_features.json';

import state_shipment_norm from './resource/tvcg_c1/tvcg_state_shipment.json';
import state_shipment_features_norm from './resource/tvcg_c1/state_shipment_features_new.json';

// tvcg dark case -> euro countries' GDP
import euro_gdp from './resource/tvcg_dark/euro_gdp.json';
import euro_gdp_features from './resource/tvcg_dark/euro_gdp_features.json';

import euro_gdp_norm from './resource/tvcg_dark/euro_gdp_newnew.json';
import euro_gdp_features_norm from './resource/tvcg_dark/euro_gdp_features_new.json';

/** import case scripts */
import { case_scripts } from './resource/cases';

/** Main App class */
class App extends Component {
  constructor(props){
    super(props);
    
    this.dataset = {
      state_education:{
        geo: state_education,
        features: state_education_features
      },
      state_shipment:{
        geo: state_shipment,
        features: state_shipment_features
      },
      state_shipment_norm:{
        geo: state_shipment_norm,
        features: state_shipment_features_norm
      },
      euro_gdp:{
        geo: euro_gdp,
        features: euro_gdp_features
      },
      euro_gdp_norm:{
        geo: euro_gdp_norm,
        features: euro_gdp_features_norm
      },
      //county_unemployment: {
        //geo: us10m,
        //data: unemployment,
        //features: null
        //geo: county_unemployment,
        //features: county_unemployment_features
      //},
      montreal_pop_density:{
        geo: montreal_density,
        features: montreal_density_features
      },
      georgia_pctBach:{
        geo: georgia_pctBach,
        features: georgia_pctBach_features
      },
      chicago_income:{
        geo: chicago_income,
        features: chicago_income_features
      }
    };

    this.initCaseObj = JSON.parse(case_scripts["state_education"]);
    this.initCaseObjString = JSON.stringify(this.initCaseObj, null, 4);

    
    this.currentMapFeature = {
      k: null,
      color_scheme: null,
      color_scheme_name: null,
      high_GVF_name:null,
      high_moran_name: null,
      user_define_name: null,
      selectClassification: null
    };

    this.externalGeoReverseFlag = true;
    this.chicagoHardruleFlag = true;

    this.state = {
      mapDataList: ['state_education','state_shipment','state_shipment_norm','montreal_pop_density','georgia_pctBach','chicago_income','euro_gdp', 'euro_gdp_norm'],
      selectedCaseData: this.dataset['state_education'],
      colorList: {
        name: [
          'Sequential: viridis', 
          'Sequential: reds', 
          'Sequential: greens', 
          'Sequential: YlGnBu',
          'Sequential: OrRd',
          'Diverging: RdBu', 
          'Diverging: PiYG',
        ],

        3: [
          ['#5dc963', '#21918d', '#3b528b'],
            ['#fee0d2','#fc9272','#de2d26'],
            ['#e5f5e0','#a1d99b','#31a354'],
            ['#edf8b1','#7fcdbb','#2c7fb8'],
            ['#fee8c8','#fdbb84','#e34a33'],
            ['#ef8a62','#f7f7f7','#67a9cf'],
            ['#e9a3c9','#f7f7f7','#a1d76a']
          ],

        4: [
          ['#7ad151', '#22a884', '#2a788e', '#414487'],
          ['#fee5d9','#fcae91','#fb6a4a','#cb181d'],
          ['#edf8e9','#bae4b3','#74c476','#238b45'],
          ['#ffffcc','#a1dab4','#41b6c4','#225ea8'],
          ['#fef0d9','#fdcc8a','#fc8d59','#d7301f'],
          ['#ca0020','#f4a582','#92c5de','#0571b0'],
          ['#d01c8b','#f1b6da','#b8e186','#4dac26']
        ],

        5: [
          ['#8fd744', '#35b779', '#21918d', '#31688e', '#443a83'],
          ['#fee5d9','#fcae91','#fb6a4a','#de2d26','#a50f15'],
          ['#edf8e9','#bae4b3','#74c476','#31a354','#006d2c'],
          ['#ffffcc','#a1dab4','#41b6c4','#2c7fb8','#253494'],
          ['#fef0d9','#fdcc8a','#fc8d59','#e34a33','#b30000'],
          ['#ca0020','#f4a582','#f7f7f7','#92c5de','#0571b0'],
          ['#d01c8b','#f1b6da','#f7f7f7','#b8e186','#4dac26']
        ],

        6: [
          ['#9fda3a', '#4ac26d', '#1fa187', '#27808e', '#375c8d', '#46327f'],
          ['#fee5d9','#fcbba1','#fc9272','#fb6a4a','#de2d26','#a50f15'],
          ['#edf8e9','#c7e9c0','#a1d99b','#74c476','#31a354','#006d2c'],
          ['#ffffcc','#c7e9b4','#7fcdbb','#41b6c4','#2c7fb8','#253494'],
          ['#fef0d9','#fdd49e','#fdbb84','#fc8d59','#e34a33','#b30000'],
          ['#b2182b','#ef8a62','#fddbc7','#d1e5f0','#67a9cf','#2166ac'],
          ['#c51b7d','#e9a3c9','#fde0ef','#e6f5d0','#a1d76a','#4d9221']
        ],

        7: [
          ['#abdc32', '#5dc963', '#28ae80', '#21918d', '#2c728e', '#3b528b', '#472d7b'],
          ['#fee5d9','#fcbba1','#fc9272','#fb6a4a','#ef3b2c','#cb181d','#99000d'],
          ['#edf8e9','#c7e9c0','#a1d99b','#74c476','#41ab5d','#238b45','#005a32'],
          ['#ffffcc','#c7e9b4','#7fcdbb','#41b6c4','#1d91c0','#225ea8','#0c2c84'],
          ['#fef0d9','#fdd49e','#fdbb84','#fc8d59','#ef6548','#d7301f','#990000'],
          ['#b2182b','#ef8a62','#fddbc7','#f7f7f7','#d1e5f0','#67a9cf','#2166ac'],
          ["#C75DAB", "#D691C1", "#E4C1D9", "#F1F1F1", "#A7D3D4", "#42B7B9", "#009B9E"]
        ],

        8: [
          ['#fde725', '#a0da39', '#4ac16d', '#1fa187', '#277f8e', '#365c8d', '#46327e', '#440154'],
          ['#fff5f0','#fee0d2','#fcbba1','#fc9272','#fb6a4a','#ef3b2c','#cb181d','#99000d'],
          ['#f7fcf5','#e5f5e0','#c7e9c0','#a1d99b','#74c476','#41ab5d','#238b45','#005a32'],
          ['#ffffd9','#edf8b1','#c7e9b4','#7fcdbb','#41b6c4','#1d91c0','#225ea8','#0c2c84'],
          ['#fff7ec','#fee8c8','#fdd49e','#fdbb84','#fc8d59','#ef6548','#d7301f','#990000'],
          ['#b2182b','#d6604d','#f4a582','#fddbc7','#d1e5f0','#92c5de','#4393c3','#2166ac'],
          ['#c51b7d','#de77ae','#f1b6da','#fde0ef','#e6f5d0','#b8e186','#7fbc41','#4d9221']
        ],

        9: [
          ['#fde725', '#addc30', '#5ec962', '#28ae80', '#21918c', '#2c728e', '#3b528b', '#472d7b', '#440154'],
          ['#fff5f0','#fee0d2','#fcbba1','#fc9272','#fb6a4a','#ef3b2c','#cb181d','#a50f15','#67000d'],
          ['#f7fcf5','#e5f5e0','#c7e9c0','#a1d99b','#74c476','#41ab5d','#238b45','#006d2c','#00441b'],
          ['#ffffd9','#edf8b1','#c7e9b4','#7fcdbb','#41b6c4','#1d91c0','#225ea8','#253494','#081d58'],
          ['#fff7ec','#fee8c8','#fdd49e','#fdbb84','#fc8d59','#ef6548','#d7301f','#b30000','#7f0000'],
          ['#b2182b','#d6604d','#f4a582','#fddbc7','#f7f7f7','#d1e5f0','#92c5de','#4393c3','#2166ac'],
          ['#c51b7d','#de77ae','#f1b6da','#fde0ef','#f7f7f7','#e6f5d0','#b8e186','#7fbc41','#4d9221']
        ],

        10: [
          ['#abdc32', '#5dc963', '#28ae80', '#21918d', '#2c728e', '#3b528b', '#472d7b','#472d7b','#472d7b','#472d7b'],
          ['#abdc32', '#5dc963', '#28ae80', '#21918d', '#2c728e', '#3b528b', '#472d7b','#472d7b','#472d7b','#472d7b'],
          ['#abdc32', '#5dc963', '#28ae80', '#21918d', '#2c728e', '#3b528b', '#472d7b','#472d7b','#472d7b','#472d7b'],
          ['#abdc32', '#5dc963', '#28ae80', '#21918d', '#2c728e', '#3b528b', '#472d7b','#472d7b','#472d7b','#472d7b'],
          ['#abdc32', '#5dc963', '#28ae80', '#21918d', '#2c728e', '#3b528b', '#472d7b','#472d7b','#472d7b','#472d7b'],
          ['#abdc32', '#5dc963', '#28ae80', '#21918d', '#2c728e', '#3b528b', '#472d7b','#472d7b','#472d7b','#472d7b'],
          ['#abdc32', '#5dc963', '#28ae80', '#21918d', '#2c728e', '#3b528b', '#472d7b','#472d7b','#472d7b','#472d7b']
        ],

        11: [
          ['#abdc32', '#5dc963', '#28ae80', '#21918d', '#2c728e', '#3b528b', '#472d7b','#472d7b','#472d7b','#472d7b','#472d7b'],
          ['#abdc32', '#5dc963', '#28ae80', '#21918d', '#2c728e', '#3b528b', '#472d7b','#472d7b','#472d7b','#472d7b','#472d7b'],
          ['#abdc32', '#5dc963', '#28ae80', '#21918d', '#2c728e', '#3b528b', '#472d7b','#472d7b','#472d7b','#472d7b','#472d7b'],
          ['#abdc32', '#5dc963', '#28ae80', '#21918d', '#2c728e', '#3b528b', '#472d7b','#472d7b','#472d7b','#472d7b','#472d7b'],
          ['#abdc32', '#5dc963', '#28ae80', '#21918d', '#2c728e', '#3b528b', '#472d7b','#472d7b','#472d7b','#472d7b','#472d7b'],
          ['#abdc32', '#5dc963', '#28ae80', '#21918d', '#2c728e', '#3b528b', '#472d7b','#472d7b','#472d7b','#472d7b','#472d7b'],
          ['#abdc32', '#5dc963', '#28ae80', '#21918d', '#2c728e', '#3b528b', '#472d7b','#472d7b','#472d7b','#472d7b','#472d7b']
        ],

        12: [
          ["#ffffd9","#f1f9bf","#dbf1b4","#b7e3b6","#87d0bb","#59bec0","#35a8c2","#238bbb","#2168ad","#23489c","#1b2f81","#081d58"],
          ["#ffffd9","#f1f9bf","#dbf1b4","#b7e3b6","#87d0bb","#59bec0","#35a8c2","#238bbb","#2168ad","#23489c","#1b2f81","#081d58"],
          ["#ffffd9","#f1f9bf","#dbf1b4","#b7e3b6","#87d0bb","#59bec0","#35a8c2","#238bbb","#2168ad","#23489c","#1b2f81","#081d58"],
          ["#ffffd9","#f1f9bf","#dbf1b4","#b7e3b6","#87d0bb","#59bec0","#35a8c2","#238bbb","#2168ad","#23489c","#1b2f81","#081d58"],
          ["#ffffd9","#f1f9bf","#dbf1b4","#b7e3b6","#87d0bb","#59bec0","#35a8c2","#238bbb","#2168ad","#23489c","#1b2f81","#081d58"],
          ["#ffffd9","#f1f9bf","#dbf1b4","#b7e3b6","#87d0bb","#59bec0","#35a8c2","#238bbb","#2168ad","#23489c","#1b2f81","#081d58"],
          ["#ffffd9","#f1f9bf","#dbf1b4","#b7e3b6","#87d0bb","#59bec0","#35a8c2","#238bbb","#2168ad","#23489c","#1b2f81","#081d58"]
          
        ],
      },
      //["#009B9E","#42B7B9","#A7D3D4","#F1F1F1","#E4C1D9","#D691C1","#C75DAB"]
      /** vegalite script */
      selectRawCase: "state_education",
      vegaLiteSpec: JSON.parse(case_scripts["state_education"]),

      /** vegaLite raw code */
      //rawScript: case_scripts["state_education"],
      rawScript: this.initCaseObjString,
      specOld: JSON.parse(case_scripts["state_education"]),
      specOldText: this.initCaseObjString,

      /** Code Editor View controller */
      editorView: "Editor",

      /** for operation history map */
      specHistory: null,
      oldSelectRawCase: "state_education",
      oldSelectedCaseData: this.dataset['state_education'],
      showHistory: false,

      /** class recommendation selection */
      classificationMeasureList: ['GVF', 'Moran'],
      currentSelectRecomm: {
        k: null,
        color_scheme: null,
        color_scheme_name: null,
        selectClassification: null
      },

      /** Hard rules detection */
      hasHardRuleViolation: false,
      hardRuleMsg:[],

      /** linter features */
      color_scheme_name: "Sequential: greens",
      softFixSpec: null,
      softRuleMsg:[],

      // map projection props
      selectProjType: "equalEarth",
      // map symbolism props
      //size: null,
      //background: null,
      //stroke: null,
      //strokeWidth: null,
      //title: null,
      reCheckColorScheme: null,
      reCheckStrokeColor: null,
      reCheckBgColor: null,

      //original measures
      originalGVF: {
        state_education: 0,
        montreal_pop_density: 0.78,
        georgia_pctBach: 0.63,
        county_unemployment: 0.81,
        chicago_income: 0.77,
        state_shipment: 0.37,
        state_shipment_norm: 0.37,
        euro_gdp: 0.79,
        euro_gdp_norm: 0.79
      },
      originalMoran: {
        state_education: 0.21,
        montreal_pop_density: 0.57,
        georgia_pctBach: 0.24,
        county_unemployment: 0.68,
        chicago_income: 0.53,
        state_shipment: 0.33,
        state_shipment_norm: 0.33,
        euro_gdp: 0.42,
        euro_gdp_norm: 0.42
      },

      //global options indicator
      globalProjHighlight: "cancelHighlight",
      globalColorHighlight: "cancelHighlight",
    };
  }

  /** class functions */

  handleglobalProjHighlightEnter = () => {
    this.setState({
      globalProjHighlight: "globalHighlight"
    });
  };

  handleglobalColorHighlightEnter = () => {
    this.setState({
      globalColorHighlight: "globalHighlight"
    });
  };

  handleglobalProjHighlightLeave = () => {
    this.setState({
      globalProjHighlight: "cancelHighlight"
    });
  };

  handleglobalColorHighlightLeave = () => {
    this.setState({
      globalColorHighlight: "cancelHighlight"
    });
  };

  setreCheckColorScheme = (val) => {
    console.log(val);
    this.setState({
        reCheckColorScheme: val
    });
  };

  setreCheckStrokeColor = (val) => {
      this.setState({
          reCheckStrokeColor: val
      });
  };

  setreCheckBgColor = (val) => {
      this.setState({
          reCheckBgColor: val
      });
  };

  // When select a new case study
  handleCaseSelection = (selectedCase) => {
    
    this.state.vegaLiteSpec.data.values = this.state.selectRawCase;
    let selectedCaseData = this.dataset[selectedCase];

    if(selectedCase === "montreal_pop_density" && this.externalGeoReverseFlag){
      this.dataset['montreal_pop_density'].geo.features.forEach(e=>{
        if(e.geometry.type === "Polygon"){
          e.geometry.coordinates[0].reverse();
        }else{
          e.geometry.coordinates.forEach(j=>{
            j[0].reverse();
          });
        }
      });
      this.externalGeoReverseFlag = false;
    }

    this.setState({
      selectRawCase: selectedCase,
      selectedCaseData: selectedCaseData,
      rawScript: case_scripts[selectedCase],
      vegaLiteSpec: JSON.parse(case_scripts[selectedCase]),
      specOld: JSON.parse(case_scripts[selectedCase]),
      oldSelectRawCase: selectedCase,
      oldSelectedCaseData: selectedCaseData,
      specOldText: case_scripts[selectedCase],
      currentSelectRecomm: {
        k: null,
        color_scheme: null,
        color_scheme_name: null,
        selectClassification: null
      },
      hasHardRuleViolation: false,
      hardRuleMsg:[],
      softFixSpec: null,
      selectProjType: "equalEarth",
      reCheckColorScheme: null,
      reCheckStrokeColor: null,
      reCheckBgColor: null
    });
    
  };

  mapOptionSetting = (val) => {
    console.log(val);
    //let size = val.size === null ? null : val.size;
    let background = val.background === null ? null : val.background;
    let stroke = val.stroke === null ? null : val.stroke;
    let strokeWidth = val.strokeWidth === null ? null : val.strokeWidth;
    let title = val.title === null ? null : val.title;

    this.state.vegaLiteSpec.data.values = this.state.selectRawCase;
    let subMapSpec = JSON.parse(JSON.stringify(this.state.vegaLiteSpec));
    if(background !== null){
      subMapSpec.background = background;
    }
    if(stroke !== null){
      subMapSpec.encoding.stroke = {
        "value": stroke
      };
    }
    if(strokeWidth !== null){
      subMapSpec.encoding.strokeWidth = {
        "value": strokeWidth
      };
    }
    if(title !== null){
      subMapSpec.title = title
    }

    this.setState({
      vegaLiteSpec: subMapSpec,
      rawScript: JSON.stringify(subMapSpec, null, 4)
    });
    
  };

  handleRecommendMethodSelection = (val) => {
    this.setState({
      currentSelectRecomm: val
    });
  };

  handleMapProjChange = (value) => {
    this.setState({
        selectProjType: value
    });

    this.state.vegaLiteSpec.data.values = this.state.selectRawCase;
    let specTemp = JSON.parse(JSON.stringify(this.state.vegaLiteSpec));
    specTemp.projection = {
      "type": value
    };
    this.setState({
      //vegaLiteSpec: specTemp,
      rawScript: JSON.stringify(specTemp, null, 4)
    });
  };

  // dynamically set selected recommendations
  handleCurrentMeasuresChange = (gvfName, moranName) => {
    this.currentMapFeature.high_GVF_name = gvfName;
    this.currentMapFeature.high_moran_name = moranName;
  };

  handleCurrentColorChange = (color, color_name) => {
    this.currentMapFeature.color_scheme = color;
    this.currentMapFeature.color_scheme_name = color_name;
  };

  handleCurrentKChange = (k) => {
    this.currentMapFeature.k = k;
  };

  handleCurrentClassificationChange = (val) => {
    this.currentMapFeature.selectClassification = val;
  };

  handleSoftFix = (info) => {
    this.setState({
      softFixSpec: info
    });
    this.state.vegaLiteSpec.data.values = this.state.selectRawCase;
    let specTemp = JSON.parse(JSON.stringify(this.state.vegaLiteSpec));
    specTemp.encoding.color.scale.domain = info.breaks;
    specTemp.encoding.color.scale.range = info.color_scheme;

    this.setState({
      //vegaLiteSpec: specTemp,
      rawScript: JSON.stringify(specTemp, null, 4)
    });
    
  };

  // When click the "Run Script" btn
  handleScriptRunClick = () => {
    // store the old spec string for the last step
    this.state.vegaLiteSpec.data.values = this.state.selectRawCase;
    this.setState({
      hasHardRuleViolation: false,
      hardRuleMsg: []
    });
    //parse the string spec into real Vega spec
    try{
      let changedScript = JSON.parse(this.state.rawScript);  
      this.setState({
        vegaLiteSpec: changedScript
      });
    }catch(err){
      //console.log(err);
      this.handleVegaParseError(err, false);
    }
    //console.log(changedScript);
  };

  // when change the content in code editor
  handleEditorChange =(newValue)=>{
    this.setState({
        rawScript: newValue
    });
  };

  // When switch between editor and diff viewer
  handleEditorViewSwitch = (switchKey) => {
    let newView = switchKey.target.value;
    this.setState({
      editorView: newView
    });
  }

  handleVegaParseError = (err, isVegaSpec) => {
    let hardRuleMsg = {
      title: "",
      type: "",
      text: "",
      lineNum: null
    };
    let hardRuleMsgList = [];
    if(isVegaSpec){ // spec hard error
      hardRuleMsg.title = "Invalid specification: ";
      hardRuleMsg.type = "spec";
      hardRuleMsg.text = ""; // replace with the msg in features.json

      hardRuleMsgList.push(hardRuleMsg);
      console.log(err);
      this.setState({
        hasHardRuleViolation: true,
        hardRuleMsg: hardRuleMsgList
      });
      
    }else{ // json syntax error
      hardRuleMsg.title = "SyntaxError: ";
      hardRuleMsg.type = "syntax";
      //hardRuleMsg.text = err.message;
      // get line number of the error
      let mseTemp = err.message.split(" ");
      hardRuleMsg.lineNum = mseTemp[mseTemp.length - 7]-1;
      // reconstruct the error msg
      let errorMsg = "";
      mseTemp.forEach((e, i)=>{
        if(i !== mseTemp.length - 5 && i !== mseTemp.length - 6){
          if(i === mseTemp.length - 7){
            errorMsg = errorMsg + (e-1) + " ";
          }else{
            errorMsg = errorMsg + e + " ";
          }
        }
      });
      hardRuleMsg.text = errorMsg;
      hardRuleMsgList.push(hardRuleMsg);
      // update the the hard errors in the state
      this.setState({
        hasHardRuleViolation: true,
        hardRuleMsg: hardRuleMsgList
      });
    }
  };

  getSpecLineNum = (specString, key) => {
    let index = specString.indexOf(key);
    let tempString = specString.substring(0, index);
    let lineNumber = tempString.split('\n').length;

    return lineNumber;
  }

  checkMapHardRule = (spec) => {
    let hardRuleViolation = [];
    spec.data.values = this.state.selectRawCase;
    let hasHardRuleViolation = false;

    //1. schema
    if(!("$schema" in spec)){
      let hardRuleMsg = {};
      hardRuleMsg.lineNum = 1;
      hardRuleMsg.title = "Invalid specification: ";
      hardRuleMsg.type = "spec";
      hardRuleMsg.text = "The specification should include the propertie <$schema>.";
      hardRuleMsg.fix = {
        parent: [],
        key: "$schema",
        value: "https://vega.github.io/schema/vega-lite/v5.json"
      };
      hardRuleMsg.fixSuggestion = "Add '$schema' with a value as a property of the specification.";
      hardRuleViolation.push(hardRuleMsg);
      hasHardRuleViolation = true;
    }
    if(!("format" in spec["data"])){
      let hardRuleMsg = {};
      let specString = JSON.stringify(spec, null, 4);
      let lineNum = this.getSpecLineNum(specString, "data");
      hardRuleMsg.lineNum = lineNum;
      hardRuleMsg.title = "Invalid specification: ";
      hardRuleMsg.type = "spec";
      hardRuleMsg.text = "The <data> property should include <format>.";
      hardRuleMsg.fix = {
        parent: ['data'],
        key: "format",
        value: {"property": "features"}
      };
      hardRuleMsg.fixSuggestion = "Add <'format': {'property': 'features'}> for the <data> property.";
      hardRuleViolation.push(hardRuleMsg);
      hasHardRuleViolation = true;
    }
    if(!("mark" in spec)){
      let hardRuleMsg = {};
      hardRuleMsg.lineNum = 1;
      hardRuleMsg.title = "Invalid specification: ";
      hardRuleMsg.type = "spec";
      hardRuleMsg.text = "The specification should include the propertie <mark: geoshape>.";
      hardRuleMsg.fix = {
        parent: [],
        key: "mark",
        value: "geoshape"
      };
      hardRuleMsg.fixSuggestion = "Add <'mark': 'geoshape'> for the specification.";
      hardRuleViolation.push(hardRuleMsg);
      hasHardRuleViolation = true;
    }
    if(!("field" in spec["encoding"]["color"])){
      let hardRuleMsg = {};
      let specString = JSON.stringify(spec, null, 4);
      let lineNum = this.getSpecLineNum(specString, "color");
      hardRuleMsg.lineNum = lineNum;
      hardRuleMsg.title = "Invalid specification: ";
      hardRuleMsg.type = "spec";
      hardRuleMsg.text = "The <encoding.color> property should have the property <field>.";
      hardRuleMsg.fix = {
        parent: ['encoding', 'color'],
        key: "field",
        value: "properties.higher_education_rate" //should be user-defined
      };
      hardRuleMsg.fixSuggestion = "Add <'field'> property with the corresponding data field from the GeoJSON data.";
      hardRuleViolation.push(hardRuleMsg);
      hasHardRuleViolation = true;
    }
    if(!("type" in spec["encoding"]["color"])){
      let hardRuleMsg = {};
      let specString = JSON.stringify(spec, null, 4);
      let lineNum = this.getSpecLineNum(specString, "color");
      hardRuleMsg.lineNum = lineNum;
      hardRuleMsg.title = "Invalid specification: ";
      hardRuleMsg.type = "spec";
      hardRuleMsg.text = "The <encoding.color> property should have the property <type>.";
      hardRuleMsg.fix = {
        parent: ['encoding', 'color'],
        key: "type",
        value: "quantitative" //should be user-defined
      };
      hardRuleMsg.fixSuggestion = "Add <'type': 'quantitative'> property to the <color> property.";
      hardRuleViolation.push(hardRuleMsg);
      hasHardRuleViolation = true;
    }else if(spec["encoding"]["color"]["type"] !== "quantitative"){
      let hardRuleMsg = {};
      let specString = JSON.stringify(spec, null, 4);
      let lineNum = this.getSpecLineNum(specString, "color")+2;
      hardRuleMsg.lineNum = lineNum;
      hardRuleMsg.title = "Invalid specification: ";
      hardRuleMsg.type = "spec";
      hardRuleMsg.text = "Invalid <encoding.color.type> value, please use 'quantitative' as the property value.";
      hardRuleMsg.fix = {
        parent: ['encoding', 'color'],
        key: "type",
        value: "quantitative" //should be user-defined
      };
      hardRuleMsg.fixSuggestion = "The value <encoding.color.type> property should be 'quantitative'.";
      hardRuleViolation.push(hardRuleMsg);
      hasHardRuleViolation = true;
    }

    return {hardRuleViolation, hasHardRuleViolation};
  };

  handleHardRuleFixClick = () => {
    this.state.vegaLiteSpec.data.values = this.state.selectRawCase;
    let spec = this.state.vegaLiteSpec;
    let {hardRuleViolation, hasHardRuleViolation} = this.checkMapHardRule(spec);

    let specString = JSON.stringify(spec, null, 4);
    let rawObj = JSON.parse(specString);
    hardRuleViolation.forEach(e=>{
      let fix = e.fix;
      let obj_temp = {};
      if(fix.parent.length === 0){
        rawObj[fix.key] = fix.value;
      }else{
        fix.parent.forEach((e,i)=>{
          if(i === 0){
            obj_temp = rawObj[e];
          }else{
            obj_temp = obj_temp[e]
          }
        });
        obj_temp[fix.key] = fix.value;
      }
    });
    //console.log(rawObj);
    // update the vega spec state and the spec string in code editor
    this.setState({
      rawScript: JSON.stringify(rawObj, null, 4),
      vegaLiteSpec: rawObj,
      specOld: rawObj,
      specOldText: JSON.stringify(rawObj, null, 4)
    });
  };

  getMeasures = (breaks) => {
    // make request options
    const requestOptions = {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ 
          class_breaks: breaks,
          data_list: this.state.selectedCaseData.features.data_list,
          case_name: this.state.selectRawCase,
          key_prop: this.state.selectedCaseData.features.key_prop
      })
    };
    fetch('http://127.0.0.1:5000/getfeature', requestOptions)
        .then(response => response.json())
        .then(json => {
            const responseMsg = json.msg;
            const measures = json.measures;
            
            console.log(measures);
          })
          .catch(error => {
            console.log(error);
          });

    //return requestOptions;
    
  };

  extractMapFeatures = (spec) => {
    let mapFeature = {
      width: spec.width,
      height: spec.height,
      background: spec.background,
      stroke: spec.encoding.stroke.value,
      strokeWidth: 1,
      //projection: spec.projection.type,
      
    };

    // check the class breaks and color scheme
    //default color scheme is viridis
    if("scale" in spec['encoding']['color']){
      mapFeature["color_scheme"] = spec.encoding.color.scale.range;
      mapFeature["breaks"] = spec.encoding.color.scale.domain;
      mapFeature['k'] = spec.encoding.color.scale.range.length;
      mapFeature['ifClassed'] = true;
    }else{
      // unclassed map
      mapFeature['ifClassed'] = false;
    }

    if("strokeWidth" in spec.encoding){
      mapFeature["strokeWidth"] = spec.encoding.strokeWidth.value;
    }

    // check projection
    // https://vega.github.io/vega-lite/docs/projection.html
    if("projection" in spec){
      mapFeature['projection'] = spec.projection.type;
    }else{
      mapFeature['projection'] = "equalEarth";
    }

    //check encoding.stroke
    //this.getMeasures(spec.encoding.color.scale.domain);

    // return extracted map features
    return mapFeature;
  }; 

  /** Render components for the main layout */
  render(){
    console.log(state_shipment_features);
    const { Content } = Layout;
    //console.log(this.state);
    /** Hard rule check for the spec properties */
    let spec = this.state.vegaLiteSpec;
    let {hardRuleViolation, hasHardRuleViolation} = this.checkMapHardRule(spec);
    //console.log(hardRuleViolation);
    //console.log(hasHardRuleViolation);
    let hardErrFlag = (this.state.hasHardRuleViolation || hasHardRuleViolation) ? true : false; 
    let hardErrMsg = this.state.hardRuleMsg.concat(hardRuleViolation);

    //TODO: Soft rule check
    let mapFeatureReady = null;
    let recommend_k = null;
    let recommend_color = null;
    let color_scheme_name = "Sequential: viridis";
    let originVegaSpec = null;
    let softFixSpec = this.state.softFixSpec;
    if(!hardErrFlag){
      /** record the original map spec with deceptive designs */
      originVegaSpec = JSON.parse(JSON.stringify(spec));
      /** extract map features */
      mapFeatureReady = this.extractMapFeatures(spec);
      // determine color scheme and k for the recommend charts
      if(softFixSpec !== null){
        recommend_k = softFixSpec.k;
        recommend_color = softFixSpec.color_scheme;
        color_scheme_name = softFixSpec.color_scheme_name;
      }else{
        recommend_k = (mapFeatureReady.k >= 3 && mapFeatureReady.k <= 7) ? mapFeatureReady.k : 3;
        recommend_color = (mapFeatureReady.k >= 3 && mapFeatureReady.k <= 7) ? mapFeatureReady.color_scheme : ['#5dc963', '#21918d', '#3b528b'];
        
      } 
    }

    return(
      <div className='App'>
        <Layout className='mainContainer'>
          {/** Main layout of the system */}
          <Content className='vastContainer'>
            {/** Row #1 */}
            <Row gutter={6}>
              {/** Left Main Col */}
              <Col span={7}>
                <Row gutter={[6,6]}>
                  {/** Nav Panel */}
                  <Col span={24}>
                    <NavBar
                      mapDataList={this.state.mapDataList}
                      onCaseSelection={this.handleCaseSelection}
                    />
                  </Col>
                  {/** Vega-Lite Script Editor */}
                  <Col span={24}>
                    <CodeEditor
                      vagaLiteSpecText={this.state.rawScript}
                      onScriptRunClick={this.handleScriptRunClick}
                      onEditorChange={this.handleEditorChange}
                      specOld={this.state.specOld}
                      specOldText={this.state.specOldText}
                      editorView={this.state.editorView}
                      onEditorViewSwitch={this.handleEditorViewSwitch}
                      hasHardRuleViolation={hardErrFlag}
                      hardRuleMsg={hardErrMsg}
                    />
                  </Col>
                  <Col span={24}>
                      <StatusBar 
                        mapOptionSetting={this.mapOptionSetting}
                        onMapProjChange={this.handleMapProjChange}
                        selectProjType={this.state.selectProjType}
                        globalProjHighlight={this.state.globalProjHighlight}
                        globalColorHighlight={this.state.globalColorHighlight}
                      />
                  </Col>
                </Row>
              </Col>

              {/** Middle Col */}
              <Col span={8}>
                <Row gutter={[6,6]}>
                  {/** 1st Row at the middle column - show original map */}
                  <Col span={24}>
                    <MapLinter
                      vegaLiteSpec={this.state.specOld}
                      selectRawCase={this.state.oldSelectRawCase}
                      selectedCaseData={this.state.oldSelectedCaseData}
                      onVegaParseError={this.handleVegaParseError}
                      hasHardRuleViolation={hardErrFlag}
                      state_shipment={state_shipment}
                      state_shipment_features={state_shipment_features}
                    />
                  </Col>
                  {/** Linter Report */}
                  <Col span={24}>
                    <LinterReport
                      currentSelectRecomm={this.state.currentSelectRecomm}
                      originalGVF={this.state.originalGVF[this.state.selectRawCase]}
                      originalMoran={this.state.originalMoran[this.state.selectRawCase]}
                      hasHardRuleViolation={hardErrFlag}
                      hardRuleMsg={hardErrMsg}
                      onHardRuleFixClick={this.handleHardRuleFixClick}
                      mapFeatureReady={mapFeatureReady}
                      selectedCaseData={this.state.selectedCaseData}
                      selectRawCase={this.state.selectRawCase}
                      colorList={this.state.colorList}
                      onSoftFix={this.handleSoftFix}
                      onMapProjChange={this.handleMapProjChange}
                      onRecommendMethodSelection={this.handleRecommendMethodSelection}
                      mapOptionSetting={this.mapOptionSetting}
                      reCheckColorScheme={this.state.reCheckColorScheme}
                      reCheckStrokeColor={this.state.reCheckStrokeColor}
                      reCheckBgColor={this.state.reCheckBgColor}
                      setreCheckColorScheme={this.setreCheckColorScheme}
                      setreCheckStrokeColor={this.setreCheckStrokeColor}
                      setreCheckBgColor={this.setreCheckBgColor}
                      handleglobalProjHighlightEnter={this.handleglobalProjHighlightEnter}
                      handleglobalColorHighlightEnter={this.handleglobalColorHighlightEnter}
                      handleglobalProjHighlightLeave={this.handleglobalProjHighlightLeave}
                      handleglobalColorHighlightLeave={this.handleglobalColorHighlightLeave}
                      onCaseSelection={this.handleCaseSelection}
                    />
                  </Col>
                </Row>
              </Col>

              {/** Right Col */}
              <Col span={9}>
                <Row gutter={[6,6]}>
                  <Col span={24}>
                    <SupportMapView 
                      selectRawCase={this.state.selectRawCase}
                      selectedCaseData={this.state.selectedCaseData}
                      vegaLiteSpec={this.state.vegaLiteSpec}
                      softFixSpec={this.state.softFixSpec}
                      hasHardRuleViolation={hardErrFlag}
                      selectProjType={this.state.selectProjType}
                      originalGVF={this.state.originalGVF[this.state.selectRawCase]}
                      originalMoran={this.state.originalMoran[this.state.selectRawCase]}
                      mapFeatureReady={mapFeatureReady}
                    />
                  </Col>
                  <Col span={24}>
                    <ClassRecommend
                      softFixSpec={this.state.softFixSpec}
                      hasHardRuleViolation={hardErrFlag}
                      selectRawCase={this.state.selectRawCase}
                      selectedCaseData={this.state.selectedCaseData}
                      vegaLiteSpec={this.state.vegaLiteSpec}
                      classificationMeasureList={this.state.classificationMeasureList}
                      
                      recommend_k={recommend_k}
                      recommend_color={recommend_color}
                      colorList={this.state.colorList}
                      color_scheme_name={color_scheme_name}
                      onCurrentKChange={this.handleCurrentKChange}
                      onCurrentColorChange={this.handleCurrentColorChange}
                      onCurrentMeasuresChange={this.handleCurrentMeasuresChange}
                      onRecommendMethodSelection={this.handleRecommendMethodSelection}
                    />
                    
                  </Col>
                </Row>
              </Col>
            </Row>
          </Content>
        </Layout>
      </div>
    );
  }
}

export default App;
The diff you're trying to view is too large. Only the first 1000 changed files have been loaded.
Showing with 0 additions and 0 deletions (0 / 0 diffs computed)
swh spinner

Computing file changes ...

back to top

Software Heritage — Copyright (C) 2015–2026, The Software Heritage developers. License: GNU AGPLv3+.
The source code of Software Heritage itself is available on our development forge.
The source code files archived by Software Heritage are available under their own copyright and licenses.
Terms of use: Archive access, API— Content policy— Contact— JavaScript license information— Web API