Quantcast
Channel: 懒得折腾
Viewing all 764 articles
Browse latest View live

The Hardware/Software Interface Lab 2

$
0
0

Lab 2: Instructions

 

When you’re ready to submit your solution, go to the assignments list.

 

 

Lab 2: Disassembling and Defusing a Binary Bomb

Overview

The nefarious Dr. Evil has planted a slew of “binary bombs” on our machines. A binary bomb is a program that consists of a sequence of phases. Each phase expects you to type a particular string on stdin. If you type the correct string, then the phase is defused and the bomb proceeds to the next phase. Otherwise, the bomb explodes by printing “BOOM!!!” and then terminating. The bomb is defused when every phase has been defused.

There are too many bombs for us to deal with, so we are giving everyone a bomb to defuse. Your mission, which you have no choice but to accept, is to defuse your bomb before the due date. Good luck, and welcome to the bomb squad!

Instructions

The bombs were constructed specifically for 64-bit machines. You should do this assignment on the 64-bit Linux VM and be sure it works there (or at least test your solution there before submitting it!) so that you can be sure it works when we grade it. In fact, there is a rumor that Dr. Evil has ensured the bomb will always blow up if run elsewhere. There are several other tamper-proofing devices built into the bomb as well, or so they say.

Perform an update to your course-materials directory on the VM by running the update-course command from a terminal window. On the script’s success, you should find the provided code for lab2 in your course-materials directory. As a convenience, here is an archive of the course-materials directory as of this lab assignment: lab2.tar.gz. The lab2 directory should then contain the following files:

  • bomb: The executable binary bomb
  • bomb.c: Source file with the bomb’s main routine
  • defuser.txt: File in which you write your defusing solution

Your job is to defuse the bomb. You can use many tools to help you with this; please look at the tools section for some tips and ideas. The best way is to use a debugger to step through the disassembled binary.

The bomb has 5 regular phases. The 6th phase is extra credit (worth half as much as a regular phase), and rumor has it that a secret 7th phase exists. If it does and you can find and defuse it, you will receive additional extra credit points. The phases get progressively harder to defuse, but the expertise you gain as you move from phase to phase should offset this difficulty. Nonetheless, the latter phases are not easy, so please don’t wait until the last minute to start. (If you’re stumped, check the hints section at the end of this document.)

The bomb ignores blank input lines. If you run your bomb with a command line argument, for example,

 ./bomb defuser.txt

then it will read the input lines from defuser.txt until it reaches EOF (end of file), and then switch over to stdin (standard input from the terminal). In a moment of weakness, Dr. Evil added this feature so you don’t have to keep retyping the solutions to phases you have already defused.

To avoid accidentally detonating the bomb, you will need to learn how to single-step through the assembly code and how to set breakpoints. You will also need to learn how to inspect both the registers and the memory states. One of the nice side-effects of doing the lab is that you will get very good at using a debugger. This is a crucial skill that will pay big dividends the rest of your career.

Resources

There are a number of online resources that will help you understand any assembly instructions you may encounter while examining the bomb. In particular, the programming manuals for x86-64 processors distributed by Intel and AMD are exceptionally valuable. They both describe the same ISA, but sometimes one may be easier to understand than the other.

Useful for this Lab

Not Directly Useful, but Good Brainfood Nonetheless

x86-64 Calling Conventions

As a reminder, the x86-64 ISA passes the first six arguments to a function in registers. Registers are used in the following order: rdirsirdxrcxr8r9. The return value for functions is passed in rax.

Tools (Read This!!)

There are many ways of defusing your bomb. You can examine it in great detail without ever running the program, and figure out exactly what it does. This is a useful technique, but it not always easy to do. You can also run it under a debugger, watch what it does step by step, and use this information to defuse it. This is probably the fastest way of defusing it.

We do make one request, please do not use brute force! You could write a program that will try every possible key to find the right one, but the number of possibilities is so large that you won’t be able to try them all in time.

There are many tools which are designed to help you figure out both how programs work, and what is wrong when they don’t work. Here is a list of some of the tools you may find useful in analyzing your bomb, and hints on how to use them.

  • gdb: The GNU debugger is a command line debugger tool available on virtually every platform. You can trace through a program line by line, examine memory and registers, look at both the source code and assembly code (we are not giving you the source code for most of your bomb), set breakpoints, set memory watch points, and write scripts. Here are some tips for using gdb.
    • To keep the bomb from blowing up every time you type in a wrong input, you’ll want to learn how to set breakpoints.
    • The CS:APP Student Site has a very handy gdb summary (there is also a more extensive tutorial).
    • For other documentation, type help at the gdb command prompt, or type “man gdb”, or “info gdb” at a Unix prompt. Some people also like to run gdb under gdb-mode in emacs
  • objdump -t bomb: This will print out the bomb’s symbol table. The symbol table includes the names of all functions and global variables in the bomb, the names of all the functions the bomb calls, and their addresses. You may learn something by looking at the function names!
  • objdump -d bomb: Use this to disassemble all of the code in the bomb. You can also just look at individual functions. Reading the assembler code can tell you how the bomb works. Although objdump -d gives you a lot of information, it doesn’t tell you the whole story. Calls to system-level functions may look cryptic. For example, a call to sscanf might appear as: 8048c36: e8 99 fc ff ff call 80488d4 <_init+0x1a0> To determine that the call was to sscanf, you would need to disassemble within gdb.
  • strings -t x bomb: This utility will display the printable strings in your bomb and their offset within the bomb.

Looking for a particular tool? How about documentation? Don’t forget, the commands apropos and man are your friends. In particular, man ascii is more useful than you’d think. If you get stumped, use the course’s discussion board.

Hints

If you’re still having trouble figuring out what your bomb is doing, here are some hints for what to think about at each stage: (1) comparison, (2) loops, (3) switch statements, (4) recursion, (5) pointers and arrays, (6) sorting linked lists.

Submitting Your Work

You will be able to submit your assignment with the submit-hw script that is bundled with the course-materials. Using the script should be straight-forward, but it does expect you to not move/rename any files from the ”course-materials” directory. Open a terminal and type the following command:

  submit-hw lab2

After calling the submit-hw script, you will be prompted for your Coursera username and then your submission password. Your submission password is NOT the same as your regular Coursera account password!!!! You may locate your submission password at the top of the assignments list page.

After providing your credentials, the submit-hw script will run some basic checks on your code. For lab 2, this means that it checks that you have passed the first 5 phases correctly.

Once confirming that you wish to submit, with a working Internet connection, the script should submit your code properly. You can go to the assignments list page to double-check that it has been submitted and check your score as well as any feedback the auto-grader gives you. You may also download your submission code from this page, if you wish.



Provincial Water Quality Monitoring Network (PWQMN) Update Scripts

$
0
0

1. The starting point is a Access Database and a shape file. Firstly, extract the 11 table from Access to tab separated txt file.

2. Run the following script.
Python PWQMN.py > 1.txt

class PWQMNStation:
    def __init__(self, STATION):
        self.STATION = STATION
        self.PPUT = {}
        self.NNOTUR = {}
        self.CLIDUR = {}
        self.RSP = {}
    def addPPUT(self, date, value):
        if self.PPUT.has_key(date):
            self.PPUT[date] = self.PPUT[date] + "," + value
        else:
            self.PPUT[date] = value
    def addNNOTUR(self, date, value):
        if self.NNOTUR.has_key(date):
            self.NNOTUR[date] = self.NNOTUR[date] + "," + value
        else:
            self.NNOTUR[date] = value
    def addCLIDUR(self, date, value):
        if self.CLIDUR.has_key(date):
            self.CLIDUR[date] = self.CLIDUR[date] + "," + value
        else:
            self.CLIDUR[date] = value
    def addRSP(self, date, value):
        if self.RSP.has_key(date):
            self.RSP[date] = self.RSP[date] + "," + value
        else:
            self.RSP[date] = value
    def __str__(self):
        available = len(self.CLIDUR) + len(self.NNOTUR) + len(self.PPUT) + len(self.RSP)
        #str1 = "INSERT INTO PWQMN_DATA VALUES ("
        str1 = ""
#        str1 = str1 + "\"" + self.STATION + "\"" + "," + str(len(self.CLIDUR)) + "," + str(len(self.NNOTUR)) + "," + str(len(self.PPUT)) + "," + str(len(self.RSP))  + "," + str(available)
        str1 = str1 + self.STATION + "," + str(len(self.CLIDUR)) + "," + str(len(self.NNOTUR)) + "," + str(len(self.PPUT)) + "," + str(len(self.RSP))  + "," + str(available)
        str2 = "";
        for key in sorted(self.CLIDUR.iterkeys()):
            value = self.CLIDUR[key]
            if not ("," in value):
                arr = [value]
            else:
                arr = value.split(",")
            for value in arr:
                if len(value) > 0 and value[-1] == "0":
                    value = value[:-1]
                if len(value) > 0 and value[-1] == "0":
                    value = value[:-1]
                if len(value) > 0 and value[-1] == ".":
                    value = value[:-1]
                str2 = str2 + key + ":" + value + ";"

        str1 = str1 + "," + str(len(str2)) + "," + str2[:-1]

        str2 = "";
        for key in sorted(self.NNOTUR.iterkeys()):
            value = self.NNOTUR[key]
            if not ("," in value):
                arr = [value]
            else:
                arr = value.split(",")
            for value in arr:
                if len(value) > 0 and value[-1] == "0":
                    value = value[:-1]
                if len(value) > 0 and value[-1] == "0":
                    value = value[:-1]
                if len(value) > 0 and value[-1] == ".":
                    value = value[:-1]
                str2 = str2 + key + ":" + value + ";"

        #str1 = str1 + "\t" + str(len(str2)) + "\t" + str2[:-1]
        str1 = str1 + "," + str(len(str2)) + "," + str2[:-1]
        str2 = "";
        for key in sorted(self.PPUT.iterkeys()):
            value = self.PPUT[key]
            if not ("," in value):
                arr = [value]
            else:
                arr = value.split(",")
            for value in arr:
                if len(value) > 0 and value[-1] == "0":
                    value = value[:-1]
                if len(value) > 0 and value[-1] == "0":
                    value = value[:-1]
                if len(value) > 0 and value[-1] == ".":
                    value = value[:-1]
                str2 = str2 + key + ":" + value + ";"

        #tr1 = str1 + "\t" + str(len(str2)) + "\t" + str2[:-1]
        str1 = str1 + "," + str(len(str2)) + "," + str2[:-1]
        str2 = "";
        for key in sorted(self.RSP.iterkeys()):
            value = self.RSP[key]
            if not ("," in value):
                arr = [value]
            else:
                arr = value.split(",")
            for value in arr:
                if len(value) > 0 and value[-1] == "0":
                    value = value[:-1]
                if len(value) > 0 and value[-1] == "0":
                    value = value[:-1]
                if len(value) > 0 and value[-1] == ".":
                    value = value[:-1]
                str2 = str2 + key + ":" + value + ";"

        #str1 = str1 + "\t" + str(len(str2)) + "\t" + str2[:-1]
        str1 = str1 + "," + str(len(str2)) + "," + str2[:-1]
        return str1 #+ ");"
class PWQNMDataSet:
    def __init__(self):
        self.PWQMNStations = {}
    def addData(self, inputFile, yearInput):
        import fileinput
        i = 0
        for line in fileinput.input(inputFile):
            i = i + 1
            if i < 2:
                continue
            items = line.strip().split("\t")
            PARM = items[1][1:-1]
            if not (PARM in ["PPUT", "NNOTUR", "CLIDUR", "RSP"]):
                continue
            #print line.strip()
            #if True:
            #    continue
            STATION = items[0][1:-1]
            DATE = items[3] #14/1/2002
            strs = DATE.split("/")
            year = strs[2][2:-8]
            month = strs[1]
            if len(month) == 1:
                month = "0" + month
            day = strs[0]
            if len(day) == 1:
                day = "0" + day
            DATE = year + month + day
            VALUE = items[7]
            if (yearInput == 2011):
                VALUE = items[6]
            #print VALUE
            if self.PWQMNStations.has_key(STATION):
                #self.PWQMNStations[STATION] = self.PWQMNStations[STATION] + line
                station = self.PWQMNStations[STATION]
                if (PARM == "PPUT"):
                    station.addPPUT(DATE, VALUE)
                if (PARM == "NNOTUR"):
                    station.addNNOTUR(DATE, VALUE)
                if (PARM == "CLIDUR"):
                    station.addCLIDUR(DATE, VALUE)
                if (PARM == "RSP"):
                    station.addRSP(DATE, VALUE)
            else:
                station = PWQMNStation(STATION)
                if (PARM == "PPUT"):
                    station.addPPUT(DATE, VALUE)
                if (PARM == "NNOTUR"):
                    station.addNNOTUR(DATE, VALUE)
                if (PARM == "CLIDUR"):
                    station.addCLIDUR(DATE, VALUE)
                if (PARM == "RSP"):
                    station.addRSP(DATE, VALUE)
                self.PWQMNStations[STATION] = station
        #print len(self.PWQMNStations)
    def __str__(self):
        #result = "STATION\tCHLORIDE\tNITRATES\tPHOSPHORUS\tSUS_SOLIDS\tAVAILABLE\tCHLORIDE_LEN\tCHLORIDE_CONT\tNITRATES_LEN\tNITRATES_CONT\tPHOSPHORUS_LEN\tPHOSPHORUS_CONT\tSUSPENDED_SOLIDS_LEN\tSUSPENDED_SOLIDS_CONT\n"
        #result = "CREATE TABLE PWQMN_DATA ( STATION  varchar(255), CHLORIDE int, NITRATES int, PHOSPHORUS int, SUS_SOLIDS int, AVAILABLE int, CHLORIDE_LEN int, CHLORIDE_CONT  varchar2(4000), NITRATES_LEN int, NITRATES_CONT  varchar2(4000), PHOSPHORUS_LEN int, PHOSPHORUS_CONT  varchar2(4000), SUSPENDED_SOLIDS_LEN int, SUSPENDED_SOLIDS_CONT  varchar2(4000));\n"
        result = "STATION,CHLORIDE,NITRATES,PHOSPHORUS,SUS_SOLIDS,AVAILABLE,CHLORIDE_LEN,CHLORIDE_CONT,NITRATES_LEN,NITRATES_CONT,PHOSPHORUS_LEN,PHOSPHORUS_CONT,SUSPENDED_SOLIDS_LEN,SUSPENDED_SOLIDS_CONT\n"
        for key, value in self.PWQMNStations.items():
            result = result + str(value) + "\n"
        return result

if __name__ == "__main__":
    pwqmn = PWQNMDataSet()
    #for year in range(2002, 2012, 1):
    for year in range(2002, 2012, 1):
        pwqmn.addData('PWQMN_' + str(year) + '_rawdata.txt', year)
    print pwqmn
    #handle1 = open("1.txt",'w+')
    #handle1.write(str(stations))
    #handle1.close();
    #stations.getTable();

3. Use SQL Loader to load the generated text to Oracle since ArcGIS can not convert this text to other formats.
Firstly, use this sql script to create a table in Oracle.
CREATE TABLE PWQMN_DATA ( STATION  varchar(255), CHLORIDE int, NITRATES int, PHOSPHORUS int, SUS_SOLIDS int, AVAILABLE int, CHLORIDE_LEN int, CHLORIDE_CONT  varchar2(4000), NITRATES_LEN int, NITRATES_CONT  varchar2(4000), PHOSPHORUS_LEN int, PHOSPHORUS_CONT  varchar2(4000), SUSPENDED_SOLIDS_LEN int, SUSPENDED_SOLIDS_CONT  varchar2(4000));

Then, run sqlldr user/password@sde control=1.ctl, log=1.log, bad=1.bad.
The 1.ctl contains the following content:

LOAD DATA
INFILE ’1.txt’
APPEND INTO TABLE pwqmn_data
FIELDS TERMINATED BY ‘,’
TRAILING NULLCOLS
( STATION CHAR(255) ,
CHLORIDE,
NITRATES,
PHOSPHORUS,
SUS_SOLIDS,
AVAILABLE,
CHLORIDE_LEN,
CHLORIDE_CONT CHAR(4000) ,
NITRATES_LEN,
NITRATES_CONT CHAR(4000) ,
PHOSPHORUS_LEN,
PHOSPHORUS_CONT CHAR(4000) ,
SUSPENDED_SOLIDS_LEN,
SUSPENDED_SOLIDS_CONT CHAR(4000)
)

4. Convert the Oracle table to file geodatabase table using ArcGIS.


Provincial Groundwater Monitoring Network (PGMN) Update Scripts

$
0
0

1. Precipitation data update

data input: a text file with all stations.

import zipfile
class PGMNPrecipitation:
    def __init__(self, file):
        import fileinput
        i = 0
        precipitation = {}
        for line in fileinput.input(file):
            i = i + 1
            if i == 1:
                continue
            items = line.strip().split("\t")
            if len(items) != 3:
                print i
                continue
            line = items[0] + "," + items[1] + "," + items[2]
            id = items[0]
            if precipitation.has_key(id):
                precipitation[id].append(line)
            else:
                precipitation[id] = [line]
        for key, value in precipitation.items():
            handle1 = open("Precipitation/" + key + ".csv",'w+')
            handle1.write("Site_ID, Date_Time, Precipitation\n")
            for line in value:
                handle1.write(line + "\n")
            handle1.close();
if __name__ == "__main__":
    stations = PGMNPrecipitation('Precipitation.txt')

Run Java to generate charts with JFreeChart library.


import java.awt.Color;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.labels.StandardXYToolTipGenerator;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.time.Hour;
import org.jfree.data.time.Second;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.xy.XYDataset;
import org.jfree.ui.ApplicationFrame;
/**
 * A time series chart.
 */
public class PGMN_Precipitation extends ApplicationFrame {
	final static String precipitation_fr = "Précipitation";
	final static String precipitation_en = "Precipitation";
	final static String site_id_en = "Site ID";
	final static String site_id_fr = "Numéro du site";
    /**
     * A demonstration application showing how to create a simple time series chart.
     *
     * @param title  the frame title.
     */
    public PGMN_Precipitation(final String title) {
        super(title);

        try{
        	  // Open the file that is the first
        	  // command line parameter
        	  FileInputStream fstream = new FileInputStream("Y:\\PGMN\\Precipitation\\1.txt");
        	  // Get the object of DataInputStream
        	  DataInputStream in = new DataInputStream(fstream);
        	  BufferedReader br = new BufferedReader(new InputStreamReader(in));
        	  String strLine;
        	  //Read File Line By Line
        	  int i = 0;
        	  while ((strLine = br.readLine()) != null)   {
        		  //System.out.println(strLine.length());
        		  if((strLine.length() < 45)||(strLine.length() > 46)){
        			  continue;
        		  }
        		  if(!strLine.contains("csv")){
        			  continue;
        		  }

        		  String wellId = strLine.substring(39, strLine.length()-4);
        		  System.out.println(wellId);
        		//Double depth = (Double)hm.get(wellId);
        		String lang = "EN";
              	final XYDataset dataset = createDataset(wellId, lang);
            	final JFreeChart chart = createChart(dataset, wellId, lang);
            	XYPlot xyplot = (XYPlot)chart.getPlot();
            	XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer)xyplot.getRenderer();
            	renderer.setSeriesPaint(0, Color.blue);
            	try {
            		ChartUtilities.saveChartAsPNG(new File("Y:\\PGMN\\Precipitation\\" + lang + "\\" + wellId + ".png"), chart, 400, 300);
            	}
            	catch (Exception e) {
            		System.err.println(e.getMessage());
            	}

            	lang = "FR";
              	final XYDataset dataset_fr = createDataset(wellId, lang);
            	final JFreeChart chart_fr = createChart(dataset_fr, wellId, lang);
            	XYPlot xyplot_fr = (XYPlot)chart_fr.getPlot();
            	XYLineAndShapeRenderer renderer_fr = (XYLineAndShapeRenderer)xyplot_fr.getRenderer();
            	renderer_fr.setSeriesPaint(0, Color.blue);
            	try {
            		ChartUtilities.saveChartAsPNG(new File("Y:\\PGMN\\Precipitation\\" + lang + "\\" + wellId + ".png"), chart_fr, 400, 300);
            	}
            	catch (Exception e) {
            		System.err.println(e.getMessage());
            	}
            	i++;
        	  }
        	  //Close the input stream
        	  in.close();
        	    }catch (Exception e){//Catch exception if any
        	  System.err.println("Error: " + e.getMessage());
        	  }
    }
    /**
     * Returns a time series of the daily EUR/GBP exchange rates in 2001 (to date), for use in
     * the JFreeChart demonstration application.
     * <P>
     * You wouldn't normally create a time series in this way.  Typically, values would
     * be read from a database.
     *
     * @return a time series.
     *
     */
    private TimeSeries createEURTimeSeries(String WellID, String lang) {
		int count = 0;
		String precipitation = "";
		if(lang.equals("EN")){
			precipitation = precipitation_en;
		}else{
			precipitation = precipitation_fr;
		}
        TimeSeries t1 = new TimeSeries(precipitation + " (mm)");
        //System.out.println (t1.getMaximumItemCount());
        Hour begin = new Hour( 1, 1, 1, 2005);
        Hour end = new Hour(23, 31, 12, 2012);
        String endStr = end.toString();
        String str = begin.toString();

        for(Hour h1 = begin;  !str.equals(endStr) ; h1 = (Hour) h1.next()){
        	Second s = new Second(0, 0, h1.getHour(), h1.getDayOfMonth(), h1.getMonth(), h1.getYear());
       		t1.addOrUpdate(s, new Double(Double.NaN));
       		str = h1.toString();
        }

        try {
        		  // Open the file that is the first
        		  // command line parameter
        		  FileInputStream fstream = new FileInputStream("Y:\\PGMN\\Precipitation\\" + WellID + ".csv");
        		  // Get the object of DataInputStream
        		  DataInputStream in = new DataInputStream(fstream);
        		  BufferedReader br = new BufferedReader(new InputStreamReader(in));
        		  String strLine;
        		  //Read File Line By Line
        		  int i = 0;
        		  while ((strLine = br.readLine()) != null)   {
        			 if(i>0){
        			  String [] temp = strLine.split(",");
        			  //System.out.println (i);
        			  String [] temp1 = (temp[1]).split(" ");  //6/10/2009 21:00:00
        			  String [] temp2 = (temp1[0]).split("/");  //6/10/2009 21:00:00
        			  int day = Integer.parseInt(temp2[0]);
        			  int month = Integer.parseInt(temp2[1]);
        			  int year = Integer.parseInt(temp2[2]);
        			  //System.out.println (i);

        			  String [] temp3 = (temp1[1]).split(":");  //6/10/2009 21:00:00
        			  int hour = Integer.parseInt(temp3[0]);
        			  int minute = Integer.parseInt(temp3[1]);
        			  int second = Integer.parseInt(temp3[2]);
        			  Second s = new Second(second, minute, hour, day, month, year);
        			  //System.out.println(strLine);
        			  //System.out.println(year + ", " + month + ", " + day + ", " + hour + ", " + minute + ", " + second);
        			  //Hour h = new Hour(hour, day, month, year);
        			  Double d = new Double(Double.NaN);
        			  //System.out.println(temp[2]);
           			  if (temp.length > 2){
           					d = Double.parseDouble(temp[2]);// - depth;
           					count ++;
           			  }
           			  t1.addOrUpdate(s, d);
        			 }
           			i = i + 1;
        		  }
        		  in.close();

        }
        catch (Exception e) {
            System.err.println(e.getMessage());
        }

        return t1;
    }

    /**
     * Creates a sample dataset.
     *
     * @return a sample dataset.
     */
    private XYDataset createDataset(String WellID, String lang) {
        final TimeSeries eur = createEURTimeSeries(WellID, lang);
        final TimeSeriesCollection dataset = new TimeSeriesCollection();
        dataset.addSeries(eur);
        return dataset;
    }

    /**
     * Creates a chart.
     *
     * @param dataset  the dataset.
     *
     * @return a chart.
     */
    private JFreeChart createChart(final XYDataset dataset, String WellID, String lang) {
    	String precipitation = "";
		if(lang.equals("EN")){
			precipitation = precipitation_en;
		}else{
			precipitation = precipitation_fr;
		}
		String site_id = "";
		if(lang.equals("EN")){
			site_id = site_id_en;
		}else{
			site_id = site_id_fr;
		}
        final JFreeChart chart = ChartFactory.createTimeSeriesChart(
        		precipitation + " (" + site_id +": " + WellID + ")",
            "Date",
            precipitation + " (mm)",
            dataset,
            true,
            true,
            false
        );
        final XYItemRenderer renderer = chart.getXYPlot().getRenderer();
        final StandardXYToolTipGenerator g = new StandardXYToolTipGenerator(
            StandardXYToolTipGenerator.DEFAULT_TOOL_TIP_FORMAT,
            new SimpleDateFormat("d-MMM-yyyy"), new DecimalFormat("0.00")
        );
        renderer.setToolTipGenerator(g);
        return chart;
    }

    // ****************************************************************************
    // * JFREECHART DEVELOPER GUIDE                                               *
    // * The JFreeChart Developer Guide, written by David Gilbert, is available   *
    // * to purchase from Object Refinery Limited:                                *
    // *                                                                          *
    // * http://www.object-refinery.com/jfreechart/guide.html                     *
    // *                                                                          *
    // * Sales are used to provide funding for the JFreeChart project - please    *
    // * support us so that we can continue developing free software.             *
    // ****************************************************************************

    /**
     * Starting point for the demonstration application.
     *
     * @param args  ignored.
     */
    public static void main(final String[] args) {

        final PGMN_Precipitation demo = new PGMN_Precipitation("Time Series Demo 8");
        /*demo.pack();
        RefineryUtilities.centerFrameOnScreen(demo);
        demo.setVisible(true);*/

    }

}

2. Water Chemistry
data input: two tables in Oracle.

# -*- coding: utf-8 -*-
import cx_Oracle
from datetime import date

class Sample:
    def __init__(self, PGMN_WELL, SAMPLENUM, SAMPLE_DAT, CONFIDENCE, COMMENTS, IONIC_BALA, LAB, LAB_ID):
        self.PGMN_WELL = PGMN_WELL
        self.SAMPLENUM = SAMPLENUM
        self.SAMPLE_DAT = SAMPLE_DAT
        self.CONFIDENCE = CONFIDENCE
        self.COMMENTS = COMMENTS
        self.IONIC_BALA = IONIC_BALA
        self.LAB = LAB
        self.LAB_ID = LAB_ID

class Result:
    def __init__(self, SAMPLENUM, LISTID, MATRIX, PARMNAME, DILUTION, REPORT_VAL, UNITS, VALQUALIFI, REMARK1, REMARK2):
        self.SAMPLENUM = SAMPLENUM
        self.LISTID = LISTID
        self.MATRIX = MATRIX
        self.PARMNAME = PARMNAME
        self.DILUTION = DILUTION
        self.REPORT_VAL = REPORT_VAL
        self.UNITS = UNITS
        self.VALQUALIFI = VALQUALIFI
        self.REMARK1 = REMARK1
        self.REMARK2 = REMARK2
EN_FR_Dict = {"Aluminum": "Aluminium","Antimony": "Antimoine","Arsenic": "Arsenic","Barium": "Baryum","Beryllium": "B&eacute;ryllium","Boron": "Bore","Cadmium": "Cadmium","Chromium": "Chrome","Cobalt": "Cobalt","Copper": "Cuivre","Iron": "Fer","Lead": "Plomb","Manganese": "Mangan&egrave;se","Molybdenum": "Molybd&egrave;ne","Nickel": "Nickel","Selenium": "S&eacute;l&eacute;nium","Silver": "Argent","Strontium": "Strontium","Thallium": "Thallium","Titanium": "Titane","Uranium": "Uranium","Vanadium": "Vanadium","Zinc": "Zinc","Fluoride": "Fluorure","Sulphate": "Sulfate","Solids; dissolved": "Solides; dissous","Anions": "Anions","Cations": "Cations","Conductivity Estimated": "Estimation de la conductivit&eacute; ","Ion balance calculation": "Calcul de l'&eacute;quilibre ionique","Solids; Dissolved Estimated": "Solides; estimation des solides dissous ","Calcium": "Calcium","Hardness": "Titre hydrotim&eacute;trique","Magnesium": "Magn&eacute;sium","Potassium": "Potassium","Sodium": "Sodium","Alkalinity; total fixed endpt": "Alcalinit&eacute;; point limite d'alcalinit&eacute; totale","Conductivity": "Conductivit&eacute;","pH": "pH","Langeliers index calculation": "Calcul de l'index de Langelier","Saturation pH Estimated": "Estimation du pH de saturation","Nitrogen; ammonia+ammonium": "Nitrog&egrave;ne; ammoniac+ammonium","Nitrogen; nitrate+nitrite": "Nitrog&egrave;ne; nitrate+nitrite","Nitrogen; nitrite": "Nitrog&egrave;ne; nitrite","Phosphorus; phosphate": "Phosphore; phosphate","Nitrogen; total Kjeldahl": "Nitrog&egrave;ne; azote total Kjeldahl","Phosphorus; total": "Phosphore; total","Carbon; dissolved inorganic": "Carbone; carbone inorganique dissous","Carbon; dissolved organic": "Carbone; carbone organique dissous","Silicon; reactive silicate": "Silicium; silicate r&eacute;actif","Chloride": "Chlorure","Bromide": "Bromure","Iodide (I-)": "Ion iodure (I-)","Nitrogen; nitrate": "Nitrog&egrave;ne; nitrate"}
Chem_EN = ["Aluminum", "Antimony", "Arsenic", "Barium", "Beryllium", "Boron", "Cadmium", "Chromium", "Cobalt", "Copper", "Iron", "Lead", "Manganese", "Molybdenum", "Nickel", "Selenium", "Silver", "Strontium", "Thallium", "Titanium", "Uranium", "Vanadium", "Zinc", "Fluoride", "Sulphate", "Solids; dissolved", "Anions", "Cations", "Conductivity Estimated", "Ion balance calculation", "Solids; Dissolved Estimated", "Calcium", "Hardness", "Magnesium", "Potassium", "Sodium", "Alkalinity; total fixed endpt", "Conductivity", "pH", "Langeliers index calculation", "Saturation pH Estimated", "Nitrogen; ammonia+ammonium", "Nitrogen; nitrate+nitrite", "Nitrogen; nitrite", "Phosphorus; phosphate", "Nitrogen; total Kjeldahl", "Phosphorus; total", "Carbon; dissolved inorganic", "Carbon; dissolved organic", "Silicon; reactive silicate", "Chloride", "Bromide", "Iodide (I-)", "Nitrogen; nitrate"]

def calculateHeadTable(resultDict, isEnglish): 
    head_EN = "<table class='fishTable'  border='1'><tr>"
    i = 0
    for PARMNAME in Chem_EN:
        if resultDict.has_key(PARMNAME):
            if isEnglish:
                head_EN = head_EN + "<td><a href=\"#index" + str(i) + "\">" + PARMNAME + "</a></td>"
            else:
                head_EN = head_EN + "<td><a href=\"#index" + str(i) + "\">" + EN_FR_Dict[PARMNAME] + "</a></td>"            
            if i % 3 == 2:
                head_EN = head_EN + "</tr><tr>"
            i = i + 1
    if isEnglish:
        otherParameters = "Pesticides, Volatile Organic Compounds, and other parameters"
    else:
        otherParameters = "Pesticides, compos&eacute;s organiques volatiles et autres param&egrave;tres"
    if i % 3 == 1:
        head_EN = head_EN + "<td><a href=\"#index" + str(i + 1) + "\"></a></td><td><a href=\"#index" + str(i + 2) + "\"></a></td></tr><tr><td colspan=\"3\"><center><a href=\"#indexOther\">" + otherParameters + "</a></center></td></tr></table>"
    if i % 3 == 2:
        head_EN = head_EN + "<td><a href=\"#index" + str(i + 1) + "\"></a></td></tr><tr><td colspan=\"3\"><center><a href=\"#indexOther\">" + otherParameters + "</a></center></td></tr></table>"
    if i % 3 == 0:
        head_EN = head_EN + "<td colspan=\"3\"><center><a href=\"#indexOther\">" + otherParameters + "</a></center></td></tr></table>"    
    return head_EN

def calculateResultDict(well_id, sampleList, resultList): 
    def f(x): return x.PGMN_WELL == well_id
    samplesInWell = filter(f, sampleList)

    SAMPLENUMList = []
    for sample in samplesInWell:
        SAMPLENUMList.append(sample.SAMPLENUM)

    def g(x): return x.SAMPLENUM in SAMPLENUMList
    resultsInWell = filter(g, resultList)

    resultDict = {}
    for result in resultsInWell:
        if resultDict.has_key(result.PARMNAME):
            resultDict[result.PARMNAME].append(result)
        else:
            resultDict[result.PARMNAME] = [result]
    return resultDict

def calculateChartURL(results, sampleDict):
    #<img src='http://chart.apis.google.com/chart?cht=s&chd=t:21.30,42.13,45.85,86.8|33.33,42.86,100.00,19.0&chxt=x,y&chs=500x200&chxl=|0:|2002|2003|2004|2005|2006|2007|2008|2009|2010|2011|1:|0|1.05|2.10(ug/L)'/>
    UNITS = results[0].UNITS
    max = 0.02
    for result in results:
        if result.REPORT_VAL > max :
            max = result.REPORT_VAL
    if len(results) == 1:
          max = 2*max;
    
    valueString = ""
    for result in results:
        if result.REPORT_VAL is not None:
            if result.REPORT_VAL < 0.000001:
                valueString = valueString + "0,"
            else:
                valueString = valueString + ('%.2f' % (result.REPORT_VAL * 100.0 / max)) + ","
        else:
            valueString = valueString + "0,"

    beginDate = date(2002, 1, 1)
    endDate = date(2013, 1, 1)
    dateDiff = endDate - beginDate
    dateString = ""
    for result in results:
        sample = sampleDict[result.SAMPLENUM]
        sampleDate = date(sample.SAMPLE_DAT.year, sample.SAMPLE_DAT.month, sample.SAMPLE_DAT.day)
        sampleDelta = (sampleDate - beginDate).days 
        sampleDeltaPercent = sampleDelta * 100.0 / dateDiff.days
        dateString = dateString + ('%.2f' % sampleDeltaPercent) + ","
    return "<img src='http://chart.apis.google.com/chart?cht=s&chd=t:" + dateString[:-1] + "|" + valueString[:-1] + "&chxt=x,y&chs=500x200&chxl=|0:|2002|2003|2004|2005|2006|2007|2008|2009|2010|2011|2012|2013|1:|0|" + str(max*0.5) + "|" + str(max) + "(" + UNITS + ")'/>";    

def calculateDateString(SAMPLE_DAT):
    res = ""
    if SAMPLE_DAT.day < 10:
        res = res + "0" + str(SAMPLE_DAT.day)
    else:
        res = res + str(SAMPLE_DAT.day)
    res = res + "/"
    if SAMPLE_DAT.month < 10:
        res = res + "0" + str(SAMPLE_DAT.month)
    else:
        res = res + str(SAMPLE_DAT.month)
    res = res + "/"
    return res + str(SAMPLE_DAT.year)

def removeNone(val):
    if val is not None:
        return str(val)
    else:
        return ""
def calculateTable(results, sampleDict, isEnglish):
    if isEnglish:
        returnString = "<table class='fishTable'  border='1'><tr><th class=\"shaded\"><center>Date</center></th><th class=\"shaded\"><center>Value</center></th><th class=\"shaded\"><center>Units</center></th><th class=\"shaded\"><center>Qualifiers</center></th><th class=\"shaded\"><center>Remark 1</center></th><th class=\"shaded\"><center>Remark 2</center></th><th class=\"shaded\"><center>Confidence Level</center></th><th class=\"shaded\"><center>Comments</center></th><th class=\"shaded\"><center>Sample Number</center></th></tr>"
    else:
        returnString = "<table class='fishTable'  border='1'><tr><th class=\"shaded\"><center>Date</center></th><th class=\"shaded\"><center>Valeur</center></th><th class=\"shaded\"><center>Unit&eacute;</center></th><th class=\"shaded\"><center>Qualificateurs</center></th><th class=\"shaded\"><center>Observation 1</center></th><th class=\"shaded\"><center>Observation 2</center></th><th class=\"shaded\"><center>Niveau de confiance</center></th><th class=\"shaded\"><center>Commentaires</center></th><th class=\"shaded\"><center>Num&eacute;ro d'&eacute;chantillon</center></th></tr>"
    for result in results:
        sample = sampleDict[result.SAMPLENUM] 
        returnString =  returnString  + "<tr><td>" + calculateDateString(sample.SAMPLE_DAT) + "</td><td>" + str(result.REPORT_VAL) + "</td><td>" + removeNone(result.UNITS) + "</td><td>" + removeNone(result.VALQUALIFI) + "</td><td>" + removeNone(result.REMARK1) + "</td><td>" + removeNone(result.REMARK2) + "</td><td>" + removeNone(sample.CONFIDENCE) + "</td><td>" + removeNone(sample.COMMENTS) + "</td><td>" + removeNone(sample.SAMPLENUM) + "</td></tr>"
    if isEnglish:        
        returnString =  returnString  + "</table>\n<div align=\"right\"><a href=\"#top\">Back to top</a></div><br><br>"
    else:
        returnString =  returnString  + "</table>\n<div align=\"right\"><a href=\"#top\">Haut de la page</a></div><br><br>"
    return returnString

def generateCSV(well_id, resultDict, sampleDict, OtherParameters): 
    csv = "PGMN_WELL\tParameterName\tSampleDate\tValue\tUnits\tQualifiers\tRemark1\tRemark2\tConfidenceLevel\tComments\tIonbalance\tLabName\tSampleNumber\n"
    for PARMNAME in Chem_EN:
        if resultDict.has_key(PARMNAME):
            results = resultDict[PARMNAME]
            def compare(x, y): return (sampleDict[x.SAMPLENUM].SAMPLE_DAT - sampleDict[y.SAMPLENUM].SAMPLE_DAT).days
            results = sorted(results, cmp=compare)
            for result in results:
                sample = sampleDict[result.SAMPLENUM] 
                csv =  csv  + well_id + "\t" + PARMNAME + "\t" + calculateDateString(sample.SAMPLE_DAT) + "\t" + str(result.REPORT_VAL) + "\t" + removeNone(result.UNITS) + "\t" + removeNone(result.VALQUALIFI) + "\t" + removeNone(result.REMARK1) + "\t" + removeNone(result.REMARK2) + "\t" + removeNone(sample.CONFIDENCE) + "\t" + removeNone(sample.COMMENTS)  + "\t" + removeNone(sample.IONIC_BALA)  + "\t" + removeNone(sample.LAB) + "\t" + removeNone(sample.SAMPLENUM) + "\n"
    for PARMNAME in OtherParameters:
        results = resultDict[PARMNAME]
        def compare(x, y): return (sampleDict[x.SAMPLENUM].SAMPLE_DAT - sampleDict[y.SAMPLENUM].SAMPLE_DAT).days
        results = sorted(results, cmp=compare)
        for result in results:
            sample = sampleDict[result.SAMPLENUM] 
            csv =  csv  + well_id + "\t" + PARMNAME + "\t" + calculateDateString(sample.SAMPLE_DAT) + "\t" + str(result.REPORT_VAL) + "\t" + removeNone(result.UNITS) + "\t" + removeNone(result.VALQUALIFI) + "\t" + removeNone(result.REMARK1) + "\t" + removeNone(result.REMARK2) + "\t" + removeNone(sample.CONFIDENCE) + "\t" + removeNone(sample.COMMENTS)  + "\t" + removeNone(sample.IONIC_BALA)  + "\t" + removeNone(sample.LAB) + "\t" + removeNone(sample.SAMPLENUM) + "\n"
    handle1 = open("Y:\\PGMN\\WaterChemistry\\" + well_id + ".txt",'w+')
    handle1.write(csv)
    handle1.close();

def generateHTML(isEnglish, well_id, resultDict, sampleDict):
    html = ""
    if isEnglish:
        html = "<h2>Water Chemistry  in PGMN Well: " + well_id + "</h2>\n<a name=\"top\"></a><a href=\"../" + well_id + ".txt\">Water Chemistry Data Download (Tab Separated Text File)</a>\n" + calculateHeadTable(resultDict, isEnglish)
    else:
        html = "<h2>Composition chimique de l'eau des puits du R&eacute;seau provincial de contr&ocirc;le des eaux souterraines: " + well_id + "</h2>\n<a name=\"top\"></a><a href=\"../" + well_id + ".txt\">T&eacute;l&eacute;chargement des donn&eacute;es hydro-chimiques (Fichier texte s&eacute;par&eacute;)</a>\n" + calculateHeadTable(resultDict, isEnglish)
    i = 0
    for PARMNAME in Chem_EN:
        if resultDict.has_key(PARMNAME):
            if isEnglish:
                html = html + "<h3><a name=\"index" + str(i) + "\">" + PARMNAME + "</a></h3>\n"
            else:
                html = html + "<h3><a name=\"index" + str(i) + "\">" + EN_FR_Dict[PARMNAME] + "</a></h3>\n"
            results = resultDict[PARMNAME]
            def compare(x, y): return (sampleDict[x.SAMPLENUM].SAMPLE_DAT - sampleDict[y.SAMPLENUM].SAMPLE_DAT).days
            results = sorted(results, cmp=compare)
            html = html + calculateChartURL(results, sampleDict) + "\n"
            html = html + calculateTable(results, sampleDict, isEnglish) + "\n"
            i = i + 1
    if isEnglish:
        html = html + "<h3><a name=\"indexOther\">Pesticides, Volatile Organic Compounds, and other parameters</a></h3>"
    else:
        html = html + "<h3><a name=\"indexOther\">Pesticides, compos&eacute;s organiques volatiles et autres param&egrave;tres</a></h3>"
    OtherParameters = []
    for PARMNAME in sorted(resultDict.iterkeys()):
        if not PARMNAME in Chem_EN:
            OtherParameters.append(PARMNAME)
    for PARMNAME in OtherParameters:
        html = html + "<h3>" + PARMNAME + "</h3>\n"
        results = resultDict[PARMNAME]
        def compare(x, y): return (sampleDict[x.SAMPLENUM].SAMPLE_DAT - sampleDict[y.SAMPLENUM].SAMPLE_DAT).days
        results = sorted(results, cmp=compare)
        html = html + calculateTable(results, sampleDict, isEnglish) + "\n"
    if isEnglish:
        text_file = open("PGMNChemistryEN.htm", "r")
    else:
        text_file = open("PGMNChemistryFR.htm", "r")
    template = text_file.read()
    text_file.close()
    template = template.replace("${CONTENT}", html)
    if isEnglish:
        handle1 = open("Y:\\PGMN\\WaterChemistry\\EN\\" + well_id + ".htm",'w+')
    else:
        handle1 = open("Y:\\PGMN\\WaterChemistry\\FR\\" + well_id + ".htm",'w+')
    handle1.write(template)
    handle1.close();
    if isEnglish:
        generateCSV(well_id, resultDict, sampleDict, OtherParameters)   
connection = cx_Oracle.connect('EMRB_PGMN/xxxxxxx@sde')
cursor = connection.cursor() 
cursor.execute('SELECT * FROM PGMN_WELLS_SAMPLES')
rows = cursor.fetchall()
sampleDict = {}
sampleList = []
PGMN_WELLList = []
for row in rows:
    sample = Sample(row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8])
    sampleList.append(sample)
    PGMN_WELLList.append(row[1])
    if sampleDict.has_key(sample.SAMPLENUM):
        raise ValueError("Sample table should use sample number as unique key. ")
    else:
        sampleDict[sample.SAMPLENUM] = sample
PGMN_WELLSet = set(PGMN_WELLList)

cursor.execute('SELECT * FROM PGMN_WELLS_RESULTS')
rows = cursor.fetchall()

resultList = []
for row in rows:
    result = Result(row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8], row[9], row[10])
    resultList.append(result)

CHEM_CONTE = "PGMN_WELL\tCHEM_CONTE\n"
for well_id in PGMN_WELLSet:
#for well_id in ["W0000271-1"]:
    resultDict = calculateResultDict(well_id, sampleList, resultList)
    generateHTML(True, well_id, resultDict, sampleDict)
    generateHTML(False, well_id, resultDict, sampleDict)
    def f(x): return x.PGMN_WELL == well_id
    def g(x): return x.SAMPLE_DAT
    def h(x): return calculateDateString(x)
    CHEM_CONTE = CHEM_CONTE + well_id + "\t" + ", ".join(map(h, sorted(map(g, filter(f, sampleList))))) + "\n"
    #print well_id + ", " + str(len(resultDict))
    #print html
handle1 = open("Y:\\PGMN\\WaterChemistry\\CHEM_CONTE.txt",'w+')
handle1.write(CHEM_CONTE)
handle1.close();
#print len(set(PGMN_WELLList))
#print len(sampleList)
connection.close()

3. Water Level data

import java.awt.Color;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.labels.StandardXYToolTipGenerator;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.time.Hour;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.xy.XYDataset;
import org.jfree.ui.ApplicationFrame;
/**
 * A time series chart.
 */
public class PGMN_WaterLevel_Update extends ApplicationFrame {
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	final static String waterlevel_fr = "Niveau d'eau";
	final static String waterlevel_en = "Water Level";
    /**
     * A demonstration application showing how to create a simple time series chart.
     *
     * @param title  the frame title.
     */
    public PGMN_WaterLevel_Update(final String title) {
		super(title);
		try{
			String [] stationList = {"W0000001-1", "W0000002-1", "W0000003-1", "W0000004-1", "W0000005-1", "W0000006-1", "W0000007-2", "W0000008-1", "W0000009-1", "W0000010-1", "W0000011-1", "W0000012-1", "W0000013-1", "W0000014-1", "W0000015-1", "W0000016-3", "W0000017-2", "W0000018-1", "W0000019-1", "W0000020-1", "W0000021-1", "W0000022-1", "W0000023-1", "W0000024-2", "W0000024-4", "W0000026-1", "W0000027-1", "W0000028-2", "W0000028-4", "W0000029-1", "W0000030-1", "W0000031-1", "W0000032-1", "W0000033-1", "W0000034-1", "W0000035-5", "W0000036-1", "W0000037-1", "W0000038-1", "W0000039-1", "W0000040-1", "W0000041-1", "W0000042-1", "W0000043-3", "W0000044-2", "W0000044-3", "W0000045-1", "W0000046-1", "W0000049-1", "W0000053-1", "W0000054-1", "W0000055-1", "W0000056-1", "W0000058-1", "W0000059-1", "W0000060-1", "W0000061-1", "W0000062-1", "W0000063-3", "W0000064-1", "W0000065-4", "W0000066-1", "W0000067-1", "W0000068-1", "W0000069-1", "W0000070-1", "W0000071-1", "W0000073-1", "W0000075-1", "W0000076-1", "W0000077-1", "W0000078-1", "W0000079-1", "W0000080-1", "W0000081-1", "W0000082-1", "W0000083-1", "W0000084-1", "W0000085-1", "W0000086-1", "W0000087-1", "W0000088-1", "W0000089-1", "W0000091-1", "W0000092-1", "W0000093-1", "W0000094-1", "W0000095-1", "W0000096-1", "W0000097-2", "W0000097-5", "W0000098-1", "W0000099-1", "W0000101-1", "W0000102-1", "W0000106-2", "W0000107-1", "W0000108-1", "W0000109-2", "W0000111-3", "W0000112-1", "W0000113-1", "W0000114-2", "W0000114-3", "W0000114-4", "W0000115-2", "W0000115-3", "W0000117-2", "W0000118-2", "W0000119-2", "W0000121-1", "W0000122-1", "W0000123-1", "W0000124-1", "W0000125-1", "W0000127-1", "W0000129-1", "W0000130-1", "W0000131-1", "W0000132-1", "W0000133-1", "W0000134-1", "W0000135-1", "W0000136-1", "W0000137-1", "W0000138-1", "W0000139-1", "W0000140-1", "W0000142-1", "W0000144-1", "W0000145-1", "W0000146-1", "W0000148-1", "W0000151-1", "W0000152-1", "W0000153-1", "W0000154-1", "W0000155-1", "W0000156-2", "W0000156-3", "W0000157-2", "W0000157-3", "W0000158-1", "W0000159-2", "W0000159-3", "W0000162-1", "W0000163-2", "W0000163-3", "W0000164-2", "W0000164-3", "W0000165-2", "W0000165-3", "W0000166-1", "W0000167-1", "W0000168-1", "W0000169-1", "W0000170-2", "W0000170-3", "W0000171-2", "W0000172-1", "W0000173-1", "W0000174-1", "W0000175-2", "W0000175-3", "W0000176-1", "W0000177-1", "W0000177-2", "W0000177-3", "W0000178-1", "W0000180-1", "W0000181-1", "W0000182-1", "W0000184-1", "W0000185-1", "W0000186-1", "W0000187-2", "W0000187-3", "W0000188-2", "W0000188-3", "W0000189-2", "W0000190-1", "W0000192-1", "W0000193-1", "W0000195-1", "W0000196-1", "W0000197-1", "W0000198-1", "W0000200-1", "W0000201-3", "W0000203-1", "W0000204-1", "W0000205-2", "W0000205-3", "W0000206-1", "W0000207-1", "W0000208-1", "W0000209-1", "W0000211-1", "W0000212-1", "W0000213-1", "W0000214-1", "W0000215-1", "W0000216-2", "W0000217-2", "W0000218-3", "W0000218-4", "W0000218-5", "W0000219-1", "W0000220-2", "W0000221-1", "W0000222-1", "W0000223-1", "W0000224-1", "W0000225-1", "W0000227-1", "W0000228-1", "W0000229-1", "W0000230-1", "W0000231-1", "W0000232-2", "W0000233-1", "W0000234-1", "W0000236-1", "W0000237-1", "W0000240-1", "W0000242-1", "W0000243-1", "W0000244-2", "W0000245-2", "W0000246-1", "W0000247-1", "W0000248-1", "W0000249-1", "W0000250-1", "W0000251-1", "W0000252-1", "W0000253-1", "W0000254-1", "W0000255-2", "W0000255-3", "W0000258-1", "W0000259-2", "W0000259-3", "W0000260-1", "W0000261-1", "W0000262-1", "W0000263-1", "W0000264-2", "W0000264-3", "W0000265-2", "W0000266-1", "W0000267-1", "W0000268-1", "W0000269-1", "W0000270-1", "W0000271-1", "W0000272-1", "W0000274-1", "W0000275-1", "W0000276-2", "W0000276-3", "W0000277-1", "W0000278-1", "W0000279-1", "W0000280-4", "W0000281-1", "W0000282-3", "W0000283-1", "W0000284-1", "W0000285-1", "W0000286-1", "W0000287-1", "W0000288-1", "W0000289-1", "W0000290-1", "W0000291-1", "W0000292-1", "W0000293-2", "W0000293-3", "W0000294-1", "W0000295-1", "W0000296-1", "W0000297-1", "W0000298-2", "W0000298-3", "W0000298-4", "W0000299-1", "W0000300-2", "W0000300-3", "W0000301-1", "W0000302-2", "W0000302-3", "W0000303-2", "W0000303-3", "W0000304-1", "W0000305-1", "W0000306-1", "W0000307-1", "W0000309-2", "W0000309-3", "W0000310-1", "W0000311-1", "W0000312-1", "W0000313-1", "W0000314-1", "W0000315-1", "W0000316-1", "W0000317-1", "W0000318-1", "W0000319-1", "W0000321-1", "W0000322-1", "W0000323-2", "W0000323-3", "W0000323-4", "W0000324-2", "W0000324-3", "W0000325-1", "W0000326-2", "W0000326-3", "W0000327-3", "W0000327-4", "W0000328-1", "W0000329-1", "W0000330-1", "W0000331-1", "W0000332-1", "W0000333-1", "W0000334-1", "W0000335-2", "W0000335-3", "W0000336-1", "W0000337-1", "W0000338-1", "W0000340-1", "W0000341-1", "W0000342-1", "W0000343-2", "W0000343-3", "W0000344-1", "W0000345-1", "W0000346-1", "W0000347-2", "W0000347-3", "W0000348-1", "W0000349-1", "W0000350-2", "W0000350-3", "W0000351-1", "W0000352-2", "W0000352-3", "W0000353-1", "W0000354-1", "W0000355-1", "W0000356-2", "W0000356-3", "W0000357-1", "W0000358-1", "W0000359-1", "W0000361-2", "W0000361-3", "W0000362-2", "W0000362-3", "W0000363-2", "W0000363-3", "W0000364-1", "W0000365-1", "W0000366-1", "W0000367-1", "W0000368-1", "W0000369-2", "W0000369-3", "W0000370-1", "W0000371-2", "W0000371-3", "W0000372-1", "W0000373-1", "W0000374-1", "W0000375-1", "W0000376-1", "W0000377-1", "W0000378-1", "W0000379-1", "W0000380-1", "W0000381-1", "W0000382-1", "W0000383-1", "W0000384-1", "W0000386-1", "W0000387-1", "W0000388-1", "W0000389-2", "W0000389-3", "W0000390-1", "W0000391-1", "W0000392-1", "W0000393-1", "W0000394-1", "W0000395-1", "W0000396-1", "W0000397-1", "W0000398-1", "W0000399-1", "W0000400-2", "W0000400-3", "W0000401-1", "W0000402-1", "W0000403-1", "W0000404-1", "W0000405-1", "W0000406-1", "W0000407-1", "W0000408-1", "W0000409-1", "W0000410-1", "W0000411-2", "W0000411-3", "W0000412-1", "W0000413-1", "W0000414-1", "W0000415-1", "W0000416-1", "W0000417-1", "W0000418-1", "W0000419-1", "W0000420-1", "W0000421-1", "W0000423-1", "W0000424-1", "W0000425-1", "W0000426-1", "W0000427-1", "W0000428-1", "W0000429-1", "W0000430-1", "W0000431-1", "W0000432-1", "W0000433-1", "W0000435-1", "W0000436-1", "W0000437-1", "W0000438-1", "W0000439-1", "W0000440-1", "W0000441-1", "W0000442-1", "W0000443-1", "W0000444-1", "W0000445-1", "W0000446-1", "W0000447-1", "W0000448-1", "W0000449-1", "W0000450-1", "W0000451-1", "W0000452-1", "W0000453-1", "W0000454-1", "W0000455-1", "W0000456-1", "W0000457-1", "W0000458-1", "W0000459-1", "W0000460-1", "W0000461-1", "W0000462-1", "W0000463-1", "W0000464-1", "W0000465-1", "W0000466-1", "W0000467-1", "W0000468-1", "W0000469-1", "W0000470-1", "W0000471-1", "W0000473-1", "W0000474-1", "W0000475-1", "W0000476-1", "W0000477-1", "W0000478-1", "W0000479-1", "W0000480-1", "W0000481-1", "W0000482-1", "W0000486-1"};
			//String [] stationList = {"W0000008-1"};
			/*FileInputStream fstream = new FileInputStream("Y:\\PGMN\\WaterLevel\\csv\\1a.txt");
			DataInputStream in = new DataInputStream(fstream);
			BufferedReader br = new BufferedReader(new InputStreamReader(in));
			String strLine;
			int i = 0;
			while ((strLine = br.readLine()) != null)   {
				  if(strLine.length() != 53){
					  continue;
				  }
				  String wellId = strLine.substring(39, 49);
				  */
			for (int i = 0; i < stationList.length; i++) {
				String wellId = stationList[i];
				System.out.println(wellId);
				String [] languageList = {"EN", "FR"};
				for (int j = 0; j < languageList.length; j++) {
					String lang = languageList[j];
					final XYDataset dataset = createDataset(wellId, lang);
					final JFreeChart chart = createChart(dataset, wellId, lang);
					XYPlot xyplot = (XYPlot)chart.getPlot();
					XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer)xyplot.getRenderer();
					renderer.setSeriesPaint(0, Color.blue);
					try {
						ChartUtilities.saveChartAsPNG(new File("Y:\\PGMN\\WaterLevel\\png\\" + lang + "\\" + wellId + ".png"), chart, 400, 300);
					}
					catch (Exception e) {
						System.err.println("PGMN_WaterLevel_Update: " + e.getMessage());
					}					
				}

			/*
			lang = "FR";
			final XYDataset dataset_fr = createDataset(wellId, lang);
			final JFreeChart chart_fr = createChart(dataset_fr, wellId, lang);
			XYPlot xyplot_fr = (XYPlot)chart_fr.getPlot();
			XYLineAndShapeRenderer renderer_fr = (XYLineAndShapeRenderer)xyplot_fr.getRenderer();
			renderer_fr.setSeriesPaint(0, Color.blue);
			try {
				ChartUtilities.saveChartAsPNG(new File("Y:\\PGMN\\WaterLevel\\png\\" + lang + "\\" + wellId + ".png"), chart_fr, 400, 300);
				}
				catch (Exception e) {
					System.err.println(e.getMessage());
				}*/
				//i++;
			  }
			  //Close the input stream
			  //in.close();
		}catch (Exception e){//Catch exception if any
			System.err.println("Error: " + e.getMessage());
		}
    }
    /**
     * Returns a time series of the daily EUR/GBP exchange rates in 2001 (to date), for use in
     * the JFreeChart demonstration application.
     * <P>
     * You wouldn't normally create a time series in this way.  Typically, values would
     * be read from a database.
     *
     * @return a time series.
     *
     */
    private TimeSeries createEURTimeSeries(String WellID, String lang) {
		int count = 0;
		String waterlevel = "";
		if(lang.equals("EN")){
			waterlevel = waterlevel_en;
		}else{
			waterlevel = waterlevel_fr;
		}
        TimeSeries t1 = new TimeSeries(waterlevel + " (m.a.s.l.)");
        //System.out.println (t1.getMaximumItemCount());        
        Hour begin = new Hour(1, 1, 1, 2001);
        Hour end = new Hour(23, 31, 12, 2010);
        //System.out.println(begin);
        String endStr = end.toString();
        //System.out.println(endStr);
        String str = begin.toString();
        for(Hour h1 = begin;  !str.equals(endStr) ; h1 = (Hour) h1.next()){
        	t1.addOrUpdate(h1, new Double(Double.NaN));        		
        	str = h1.toString();
        }
        try {
        		  // Open the file that is the first 
        		  // command line parameter
        		  FileInputStream fstream = new FileInputStream("Y:\\PGMN\\WaterLevel\\csv\\" + WellID + ".csv");
        		  // Get the object of DataInputStream
        		  DataInputStream in = new DataInputStream(fstream);
        		  BufferedReader br = new BufferedReader(new InputStreamReader(in));
        		  String strLine;
        		  //Read File Line By Line
        		  int i = 0;
        		  while ((strLine = br.readLine()) != null)   {
        			 if(i>0){  
        			  String [] temp = strLine.split(",");
        			  //System.out.println (i);
        			  String [] temp1 = (temp[1]).split(" ");  //6/10/2009 21:00:00
        			  String [] temp2 = (temp1[0]).split("/");  //6/10/2009 21:00:00
        			  int day = Integer.parseInt(temp2[2]);
        			  int month = Integer.parseInt(temp2[1]);
        			  int year = Integer.parseInt(temp2[0]);
        			  //System.out.println (i);

        			  String [] temp3 = (temp1[1]).split(":");  //6/10/2009 21:00:00
        			  int hour = Integer.parseInt(temp3[0]);
        			  int minute = Integer.parseInt(temp3[1]);
        			  int second = Integer.parseInt(temp3[2]);
        			  //Second s = new Second(second, minute, hour, day, month, year);
        			  Hour h = new Hour(hour, day, month, year);
        			  Double d = new Double(Double.NaN);
        			  
           			  if (temp.length > 2){
           					  d = Double.parseDouble(temp[2]);// - depth;
           					  count ++;
           			  }
           			  t1.addOrUpdate(h, d);
        			 }
           			i = i + 1;
        		  }
        		  in.close();
 
        }
        catch (Exception e) {
            System.err.println("createEURTimeSeries : " + e.getMessage());
        }
        return t1;
    }
    
    /**
     * Creates a sample dataset.
     * 
     * @return a sample dataset.
     */
    private XYDataset createDataset(String WellID, String lang) {
        final TimeSeries eur = createEURTimeSeries(WellID, lang);
        final TimeSeriesCollection dataset = new TimeSeriesCollection();
        dataset.addSeries(eur);
        return dataset;
    }
    
    /**
     * Creates a chart.
     * 
     * @param dataset  the dataset.
     * 
     * @return a chart.
     */
    private JFreeChart createChart(final XYDataset dataset, String WellID, String lang) {
    	String waterlevel = "";
		if(lang.equals("EN")){
			waterlevel = waterlevel_en;
		}else{
			waterlevel = waterlevel_fr;
		}
        final JFreeChart chart = ChartFactory.createTimeSeriesChart(
        		waterlevel + " (" + WellID + ")", 
            "Date", 
            waterlevel + " (m.a.s.l.)",
            dataset, 
            true, 
            true, 
            false
        );
        final XYItemRenderer renderer = chart.getXYPlot().getRenderer();
        final StandardXYToolTipGenerator g = new StandardXYToolTipGenerator(
            StandardXYToolTipGenerator.DEFAULT_TOOL_TIP_FORMAT,
            new SimpleDateFormat("d-MMM-yyyy"), new DecimalFormat("0.00")
        );
        renderer.setToolTipGenerator(g);
        return chart;
    }
    
    // ****************************************************************************
    // * JFREECHART DEVELOPER GUIDE                                               *
    // * The JFreeChart Developer Guide, written by David Gilbert, is available   *
    // * to purchase from Object Refinery Limited:                                *
    // *                                                                          *
    // * http://www.object-refinery.com/jfreechart/guide.html                     *
    // *                                                                          *
    // * Sales are used to provide funding for the JFreeChart project - please    * 
    // * support us so that we can continue developing free software.             *
    // ****************************************************************************
    
    /**
     * Starting point for the demonstration application.
     *
     * @param args  ignored.
     */
    public static void main(final String[] args) {

    	PGMN_WaterLevel_Update demo = new PGMN_WaterLevel_Update("Time Series Demo 8");
        /*demo.pack();
        RefineryUtilities.centerFrameOnScreen(demo);
        demo.setVisible(true);*/

    }

}

Algorithms, Part II Final Exam Part II

$
0
0

Final Exam Part II

The due date for this quiz is Mon 20 May 2013 8:59 AM PDT -0700.

To specify an array or sequence of values in an answer, you must separate the values by a single space character (with no punctuation and with no leading or trailing whitespace). For example, if the question asks for the first ten powers of two (starting at 1), the only accepted answer is:

1 2 4 8 16 32 64 128 256 512

If you wish to discuss a particular question and answer in the forums, please post the entire question and answer, including the seed (which is used by the course staff to uniquely identify the question) and the explanation (which contains the correct answer).
In accordance with the Coursera Honor Code, I (Yinhuan Yuan) certify that the answers here are my own work.

Question 1

(seed = 388464)
Which of the following statements about connectivity in an undirected graph are true? Check all that apply.
If there are two edge-disjoint paths connecting vertices u and v, then there is a simple cycle containing both u and v.
If you delete two edges from a graph G, its number of strong components can increase by at most 2.
A graph G that has a unique simple path between every pair of vertices has exactly V-1 edges.
Given a graph G and a vertex s, G is connected if and only if every vertex is connected to s.
Removing any edge from a connected graph G breaks the graph into two connected components.

Question 2

(seed = 599575)
Which of the following statements about depth-first search are guaranteed to be true? Check all that apply.
If a vertex has no edges pointing from it, it will be first in any postorder
The postorder of a digraph G is the reverse of its preorder.
If there is a unique vertex reachable from every other vertex in a digraph, it will be first in some postorder.
The reverse postorder of a digraph's reverse is the same as the postorder of the digraph.
If a digraph has a vertex with no edges pointing from it, then some vertex with no edges pointing from it will be first in any postorder.

Question 3

(seed = 694838)
Which of the following statements about minimum spanning trees (MSTs) are guaranteed to be true? Check all that apply.
Let G be a connected edge-weighted graph and let T be a MST of G. Then, the cheapest spanning tree of G besides T can be obtained by deleting the most expensive edge in T and computing the MST of the resulting graph.
Let G be a connected edge-weighted graph and let T be an MST of G. If you decrease the weight of some edge in T, then T will still be a MST.
Let G be a connected edge-weighted graph with distinct edge weights and no parallel edges. Then, the MST must contain the third cheapest edge.
Let T and T' be two MSTs of some edge-weighted graph and let a[] and b[] be the sorted array of edge weights. Then a[i] = b[i] for each i.
Let G be a connected edge-weighted graph with distinct edge weights and let (A, B) be a cut with at least 3 crossing edges. Then, the MST can contain the smallest and third smallest crossing edges without containing the second smallest crossing edge.

Question 4

(seed = 959653)
Which of the following statements about shortest paths are true? Check all that apply.
If you run the Bellman-Ford algorithm on an edge-weighted digraph with positive edge weights and the shortest path from s to t has k edges, then Bellman-Ford will require at least k passes.
Bellman-Ford finds the shortest simple path from s to every other vertex, even if the edge weights are arbritary positive or negative integers.
Bellman-Ford finds the shortest simple path from s to every other vertex, even if the edge weights are positive or negative integers, provided there are no negative cycles.
Let G be a digraph with positive edge weights. During Bellman-Ford, if you trace back the edgeTo[] entries starting from v back to s, the path has distance equal to distTo[v].
Let G be a digraph with positive edge weights. Suppose that you increase the length of an edge by x. Then, the length of the shortest path from s to t can increase by more than x.

Question 5

(seed = 392474)
Which of the following statements about maxflow and mincut are guaranteeed to be true? Check all that apply.
If all edge capacities are increased by an additive constant, then any mincut in the original network is a mincut in the modified network.
There exists a maxflow for which there is no directed cycle on which every edge carries positive flow.
Given a mincut (S, T) if the capacity of one edge is decreased by x, the value of the maxflow will decrease by exactly x.
If all edge capacities are integral, then any maxflow has integer flow on each edge.
If the capacity of some cut (S, T) equals the value of some flow f, then f is a maxflow.

Question 6

(seed = 725630)
The column on the left contains the original input of 24 strings to be sorted;
the column on the right contains the strings in sorted order; the other 7 columns contain the
contents at some intermediate step during one of the 3 radix sorting algorithms listed below.

    moth   lamb   boar   puma   ibex   toad   bass   boar   bass   
    puma   hare   bass   tuna   boar   clam   boar   dove   boar   
    ibex   carp   carp   lamb   dove   boar   carp   bass   carp   
    boar   bass   clam   toad   kiwi   gnat   clam   carp   clam   
    dove   ibex   dove   dove   carp   wren   dove   erne   dove   
    kiwi   mink   erne   sole   erne   ibex   erne   clam   erne   
    carp   lion   gnat   hare   lion   sole   gnat   hare   gnat   
    tuna   kiwi   hare   mole   lamb   mole   hare   gnat   hare   
    sole   clam   ibex   erne   gnat   mule   ibex   ibex   ibex   
    lion   slug   kiwi   mule   loon   puma   kiwi   loon   kiwi   
    lamb   gnat   lion   slug   hare   lamb   lion   lamb   lamb   
    wren   toad   lamb   moth   clam   tuna   lamb   lion   lion   
    gnat   boar   loon   kiwi   bass   erne   loon   kiwi   loon   
    loon   sole   moth   mink   moth   mink   moth   moth   mink   
    hare   mole   mole   clam   mink   lion   mole   mink   mole   
    clam   loon   mink   lion   mole   loon   mink   mole   moth   
    bass   moth   mule   wren   mule   hare   mule   mule   mule   
    oryx   dove   oryx   loon   slug   carp   oryx   slug   oryx   
    slug   wren   puma   carp   oryx   bass   puma   oryx   puma   
    mole   erne   sole   boar   toad   moth   sole   toad   slug   
    toad   oryx   slug   bass   wren   slug   slug   wren   sole   
    erne   mule   tuna   gnat   sole   dove   tuna   sole   toad   
    mink   puma   toad   ibex   tuna   kiwi   toad   tuna   tuna   
    mule   tuna   wren   oryx   puma   oryx   wren   puma   wren   
    ----   ----   ----   ----   ----   ----   ----   ----   ----   
     0      ?      ?      ?      ?      ?      ?      ?      4     


Match up each column with the corresponding sorting algorithm from the given list:

    0. Original input
    1. LSD radix sort
    2. MSD radix sort
    3. 3-way radix quicksort (no shuffle)
    4. Sorted

You may use an algorithm more than once. Your answer should be a sequence of 9 integers between
0 and 4 (starting with 0 and ending with 4) and with each integer separated by a single space.

Hint: think about algorithm invariants. Do not trace code.

Answer for Question 6

Question 7

(seed = 727971)
What is the Burrows-Wheeler inverse transform of the following?

    5  B D A C D D A A 

Your answer should be a sequence of 8 characters, with a single space
separating each character.

Answer for Question 7

Question 8

(seed = 955798)
Which problems are known to have the same asymptotic complexity as multiplying two N-bit integers? Check all that apply.
Computing the remainder when dividing one N-bit integer into an N-bit integer.
Adding two N-bit integers.
Computing the remainder when dividing an N-bit integer by 17.
Squaring an N-bit integer.
Factoring an N-bit integer.

Question 9

(seed = 908805)
Consider the following linear programming simplex tableaux with 6 equations and 8 variables:

    maximize Z
                -    3 x1                                                         +  3/4 x7    -  Z    = -156
    ---------------------------------------------------------------------------------------------------------
                +    4 x1                                   +    1 x5             +    1 x7            =   18
     +    1 x0  -    1 x1                                                         -  6/5 x7            =   24
                +  3/2 x1                        +    1 x4                        +  3/5 x7            =   42
                +  5/4 x1  +    1 x2                                              +  1/2 x7            =   48
                +    2 x1                                              +    1 x6  -  5/2 x7            =   30
                +  5/2 x1             +    1 x3                                                        =   48
            x0  ,      x1  ,      x2  ,      x3  ,      x4  ,      x5  ,      x6  ,      x7           >=    0


What is the value of variable x5 in the current basic feasible solution?
Specify your answer with two digits after the decimal place.

Answer for Question 9

Question 10

(seed = 940895)
A problem is intractable if and only if it cannot be solved in
linear time
constant time
exponential space
exponential time
polynomial time

Question 11

(seed = 899132)
You are applying for a job at a new software technology company. Your interviewer asks you
to identify which of the following tasks are known to be possible. Check all that apply.
Given an undirected graph and two vertices s and t, find a path from s to t in E + V time.
Given an undirected graph, design an E + V algorithm to find a spanning tree.
Given an undirected graph, find an edge whose removal increases the number of connected components in (E+V) E time.
Given an digraph, design an E V algorithm to find the shortest directed cycle.
Given a digraph with positive edge weights and two vertices s and t, design an E + V algorithm to find a max st-flow.

Question 12

(seed = 121999)
You are applying for a job at a new software technology company. Your interviewer asks you
to identify which of the following tasks are known to be possible. Check all that apply.
Given an array of N strings, sort them in time linear in N in the worst case.
Determine whether a pattern string of length M is a substring of a text string of length N using a constant amount of extra memory.
Compute the suffix array of a string of length N in time proportional to N log N in the worst case.
Determine whether an M-character regular expression matches an N-character text in time polynomial in M and N.
Construct a DFA corresponding to an M-character regular expression.

Use Python to create Shape file

$
0
0

ID PatientLat PatientLon DoctorLati DoctorLong
G1-2 43.72045100000 -79.33867000000 43.66356400000 -79.41844900000
G1-3 43.72256800000 -79.32789800000 43.66356400000 -79.41844900000
G1-4 43.66446500000 -79.37216000000 43.66356400000 -79.41844900000
G1-5 43.76985400000 -79.31352100000 43.67230500000 -79.37684800000
G1-6 43.68728600000 -79.30062500000 43.66356400000 -79.41844900000
G1-7 43.68968700000 -79.34179200000 43.67230500000 -79.37684800000
G2-1 43.59396500000 -79.62949900000 43.59458900000 -79.60004800000
G2-2 43.87644700000 -79.44417700000 43.60914100000 -79.58384500000
G2-3 43.70311700000 -79.44102700000 43.65287600000 -79.37691800000
G2-4 43.59065400000 -79.63389400000 43.60668600000 -79.65302500000
G2-5 43.59065400000 -79.63389400000 43.73974000000 -79.58036900000
G2-6 43.59065400000 -79.63389400000 43.46136100000 -79.68344100000
G2-7 43.63528700000 -79.57568600000 43.59458900000 -79.60004800000
G3-1 43.77090400000 -79.38210300000 43.67015300000 -79.38685300000
G3-2 43.70865600000 -79.37426300000 43.72472300000 -79.30265300000
G3-3 43.78826300000 -79.41824600000 43.78702800000 -79.41737700000
G3-4 43.77172100000 -79.36399500000 43.74203400000 -79.30991600000
G3-5 43.77172100000 -79.36399500000 43.74203400000 -79.30991600000
G3-8 43.77016400000 -79.37972900000 43.76168000000 -79.34739200000
G4-1 43.69482100000 -79.31830100000 43.74203400000 -79.30991600000
G4-3 43.66553000000 -79.37173600000 43.67229500000 -79.37684300000
G4-4 43.64785700000 -79.43724900000 43.66356400000 -79.41844900000
G4-5 43.70570100000 -79.39407100000 43.77972400000 -79.41553100000
G5-1 43.92701400000 -79.45791300000 43.75205900000 -79.54238400000
G5-2 43.85498200000 -79.41007600000 43.77972400000 -79.41553100000
G5-4 43.85498200000 -79.41007600000 43.78702800000 -79.41737700000
G5-5 43.98482900000 -79.46648800000 43.83087400000 -79.27502600000
G5-6 43.80174200000 -79.42893100000 43.67015300000 -79.38685300000
G5-7 43.89265800000 -79.45329700000 43.88948100000 -79.44110900000
G5-8 43.98485400000 -79.46650100000 43.99045800000 -79.46540400000
G6-1 43.80659000000 -79.44942900000 43.78702800000 -79.41737700000
G6-2 43.80031400000 -79.45441200000 43.74203400000 -79.30991600000
G6-4 43.83695300000 -79.48003800000 43.84251300000 -79.48308000000
G6-5 43.82263400000 -79.38114000000 43.77972400000 -79.41553100000
G6-6 43.80300800000 -79.46221800000 43.59749900000 -79.59803400000
G6-7 43.92622200000 -79.45104600000 43.89472300000 -79.44267500000
G6-8 43.91076300000 -79.46863900000 43.89472300000 -79.44267500000
G7-1 43.79081900000 -79.39107700000 43.75725500000 -79.51770200000
G7-2 43.77025300000 -79.38615000000 43.74748300000 -79.28483200000
G7-3 43.75986100000 -79.32279400000 43.77972400000 -79.41553100000
G7-4 43.66332900000 -79.41561000000 43.66356400000 -79.41844900000
G7-7 43.76325300000 -79.40937800000 43.76656900000 -79.38810800000
G8-1 43.84659900000 -79.42801400000 43.88038700000 -79.39143400000
G8-2 43.80062500000 -79.42625700000 43.74105700000 -79.60478800000
G8-3 43.85821200000 -79.43256900000 43.77841900000 -79.42228500000
G8-4 43.69483000000 -79.31830900000 43.66416600000 -79.41665200000

file = open("Patient_Doctor.txt")
PatientDoctor = {}
i = 0
while 1:
    line = file.readline()
    if not line:
        break	
    i = i + 1
    if (i == 1):
        continue
    items = line.split("\t")
    PatientDoctor[items[0]] = [[float(items[2]), float(items[1])], [float(items[4]), float(items[3])]]

import shapefile
w = shapefile.Writer(shapefile.POLYLINE)
w.field('ID','C','40')
for k,v in PatientDoctor.items():
    w.record(k)
    w.line(parts=[v])
w.save('Korean')

Used a library.

"""
shapefile.py
Provides read and write support for ESRI Shapefiles.
author: jlawhead<at>geospatialpython.com
date: 20110927
version: 1.1.4
Compatible with Python versions 2.4-3.x
"""

from struct import pack, unpack, calcsize, error
import os
import sys
import time
import array
#
# Constants for shape types
NULL = 0
POINT = 1
POLYLINE = 3
POLYGON = 5
MULTIPOINT = 8
POINTZ = 11
POLYLINEZ = 13
POLYGONZ = 15
MULTIPOINTZ = 18
POINTM = 21
POLYLINEM = 23
POLYGONM = 25
MULTIPOINTM = 28
MULTIPATCH = 31

PYTHON3 = sys.version_info[0] == 3

def b(v):
    if PYTHON3:
        if isinstance(v, str):
            # For python 3 encode str to bytes.
            return v.encode('utf-8')
        elif isinstance(v, bytes):
            # Already bytes.
            return v
        else:
            # Error.
            raise Exception('Unknown input type')
    else:
        # For python 2 assume str passed in and return str.
        return v

def u(v):
    if PYTHON3:
        if isinstance(v, bytes):
            # For python 3 decode bytes to str.
            return v.decode('utf-8')
        elif isinstance(v, str):
            # Already str.
            return v
        else:
            # Error.
            raise Exception('Unknown input type')
    else:
        # For python 2 assume str passed in and return str.
        return v

def is_string(v):
    if PYTHON3:
        return isinstance(v, str)
    else:
        return isinstance(v, basestring)

class _Array(array.array):
    """Converts python tuples to lits of the appropritate type.
    Used to unpack different shapefile header parts."""
    def __repr__(self):
        return str(self.tolist())

class _Shape:
    def __init__(self, shapeType=None):
        """Stores the geometry of the different shape types
        specified in the Shapefile spec. Shape types are
        usually point, polyline, or polygons. Every shape type
        except the "Null" type contains points at some level for
        example verticies in a polygon. If a shape type has
        multiple shapes containing points within a single
        geometry record then those shapes are called parts. Parts
        are designated by their starting index in geometry record's
        list of shapes."""
        self.shapeType = shapeType
        self.points = []

class _ShapeRecord:
    """A shape object of any type."""
    def __init__(self, shape=None, record=None):
        self.shape = shape
        self.record = record

class ShapefileException(Exception):
    """An exception to handle shapefile specific problems."""
    pass

class Reader:
    """Reads the three files of a shapefile as a unit or
    separately.  If one of the three files (.shp, .shx,
    .dbf) is missing no exception is thrown until you try
    to call a method that depends on that particular file.
    The .shx index file is used if available for efficiency
    but is not required to read the geometry from the .shp
    file. The "shapefile" argument in the constructor is the
    name of the file you want to open.

    You can instantiate a Reader without specifying a shapefile
    and then specify one later with the load() method.

    Only the shapefile headers are read upon loading. Content
    within each file is only accessed when required and as
    efficiently as possible. Shapefiles are usually not large
    but they can be.
    """
    def __init__(self, *args, **kwargs):
        self.shp = None
        self.shx = None
        self.dbf = None
        self.shapeName = "Not specified"
        self._offsets = []
        self.shpLength = None
        self.numRecords = None
        self.fields = []
        self.__dbfHdrLength = 0
        # See if a shapefile name was passed as an argument
        if len(args) > 0:
            if type(args[0]) is type("stringTest"):
                self.load(args[0])
                return
        if "shp" in kwargs.keys():
            if hasattr(kwargs["shp"], "read"):
                self.shp = kwargs["shp"]
                if hasattr(self.shp, "seek"):
                    self.shp.seek(0)
            if "shx" in kwargs.keys():
                if hasattr(kwargs["shx"], "read"):
                    self.shx = kwargs["shx"]
                    if hasattr(self.shx, "seek"):
                        self.shx.seek(0)
        if "dbf" in kwargs.keys():
            if hasattr(kwargs["dbf"], "read"):
                self.dbf = kwargs["dbf"]
                if hasattr(self.dbf, "seek"):
                    self.dbf.seek(0)
        if self.shp or self.dbf:        
            self.load()
        else:
            raise ShapefileException("Shapefile Reader requires a shapefile or file-like object.")

    def load(self, shapefile=None):
        """Opens a shapefile from a filename or file-like
        object. Normally this method would be called by the
        constructor with the file object or file name as an
        argument."""
        if shapefile:
            (shapeName, ext) = os.path.splitext(shapefile)
            self.shapeName = shapeName
            try:
                self.shp = open("%s.shp" % shapeName, "rb")
            except IOError:
                raise ShapefileException("Unable to open %s.shp" % shapeName)
            try:
                self.shx = open("%s.shx" % shapeName, "rb")
            except IOError:
                raise ShapefileException("Unable to open %s.shx" % shapeName)
            try:
                self.dbf = open("%s.dbf" % shapeName, "rb")
            except IOError:
                raise ShapefileException("Unable to open %s.dbf" % shapeName)
        if self.shp:
            self.__shpHeader()
        if self.dbf:
            self.__dbfHeader()

    def __getFileObj(self, f):
        """Checks to see if the requested shapefile file object is
        available. If not a ShapefileException is raised."""
        if not f:
            raise ShapefileException("Shapefile Reader requires a shapefile or file-like object.")
        if self.shp and self.shpLength is None:
            self.load()
        if self.dbf and len(self.fields) == 0:
            self.load()
        return f

    def __restrictIndex(self, i):
        """Provides list-like handling of a record index with a clearer
        error message if the index is out of bounds."""
        if self.numRecords:
            rmax = self.numRecords - 1
            if abs(i) > rmax:
                raise IndexError("Shape or Record index out of range.")
            if i < 0: i = range(self.numRecords)[i]
        return i

    def __shpHeader(self):
        """Reads the header information from a .shp or .shx file."""
        if not self.shp:
            raise ShapefileException("Shapefile Reader requires a shapefile or file-like object. (no shp file found")
        shp = self.shp
        # File length (16-bit word * 2 = bytes)
        shp.seek(24)
        self.shpLength = unpack(">i", shp.read(4))[0] * 2
        # Shape type
        shp.seek(32)
        self.shapeType= unpack("<i", shp.read(4))[0]
        # The shapefile's bounding box (lower left, upper right)
        self.bbox = _Array('d', unpack("<4d", shp.read(32)))
        # Elevation
        self.elevation = _Array('d', unpack("<2d", shp.read(16)))
        # Measure
        self.measure = _Array('d', unpack("<2d", shp.read(16)))

    def __shape(self):
        """Returns the header info and geometry for a single shape."""
        f = self.__getFileObj(self.shp)
        record = _Shape()
        nParts = nPoints = zmin = zmax = mmin = mmax = None
        (recNum, recLength) = unpack(">2i", f.read(8))
        shapeType = unpack("<i", f.read(4))[0]
        record.shapeType = shapeType
        # For Null shapes create an empty points list for consistency
        if shapeType == 0:
            record.points = []
        # All shape types capable of having a bounding box
        elif shapeType in (3,5,8,13,15,18,23,25,28,31):
            record.bbox = _Array('d', unpack("<4d", f.read(32)))
        # Shape types with parts
        if shapeType in (3,5,13,15,23,25,31):
            nParts = unpack("<i", f.read(4))[0]
        # Shape types with points
        if shapeType in (3,5,8,13,15,23,25,31):
            nPoints = unpack("<i", f.read(4))[0]
        # Read parts
        if nParts:
            record.parts = _Array('i', unpack("<%si" % nParts, f.read(nParts * 4)))
        # Read part types for Multipatch - 31
        if shapeType == 31:
            record.partTypes = _Array('i', unpack("<%si" % nParts, f.read(nParts * 4)))
        # Read points - produces a list of [x,y] values
        if nPoints:
            record.points = [_Array('d', unpack("<2d", f.read(16))) for p in range(nPoints)]
        # Read z extremes and values
        if shapeType in (13,15,18,31):
            (zmin, zmax) = unpack("<2d", f.read(16))
            record.z = _Array('d', unpack("<%sd" % nPoints, f.read(nPoints * 8)))
        # Read m extremes and values
        if shapeType in (13,15,18,23,25,28,31):
            (mmin, mmax) = unpack("<2d", f.read(16))
            # Measure values less than -10e38 are nodata values according to the spec
            record.m = []
            for m in _Array('d', unpack("%sd" % nPoints, f.read(nPoints * 8))):
                if m > -10e38:
                    record.m.append(m)
                else:
                    record.m.append(None)
        # Read a single point
        if shapeType in (1,11,21):
            record.points = [_Array('d', unpack("<2d", f.read(16)))]
        # Read a single Z value
        if shapeType == 11:
            record.z = unpack("<d", f.read(8))
        # Read a single M value
        if shapeType in (11,21):
            record.m = unpack("<d", f.read(8))
        return record

    def __shapeIndex(self, i=None):
        """Returns the offset in a .shp file for a shape based on information
        in the .shx index file."""
        shx = self.shx
        if not shx:
            return None
        if not self._offsets:
            # File length (16-bit word * 2 = bytes) - header length
            shx.seek(24)
            shxRecordLength = (unpack(">i", shx.read(4))[0] * 2) - 100
            numRecords = shxRecordLength // 8
            # Jump to the first record.
            shx.seek(100)
            for r in range(numRecords):
                # Offsets are 16-bit words just like the file length
                self._offsets.append(unpack(">i", shx.read(4))[0] * 2)
                shx.seek(shx.tell() + 4)
        if not i == None:
            return self._offsets[i]

    def shape(self, i=0):
        """Returns a shape object for a shape in the the geometry
        record file."""
        shp = self.__getFileObj(self.shp)
        i = self.__restrictIndex(i)
        offset = self.__shapeIndex(i)
        if not offset:
            # Shx index not available so use the full list.
            shapes = self.shapes()
            return shapes[i]
        shp.seek(offset)
        return self.__shape()

    def shapes(self):
        """Returns all shapes in a shapefile."""
        shp = self.__getFileObj(self.shp)
        shp.seek(100)
        shapes = []
        while shp.tell() < self.shpLength:
            shapes.append(self.__shape())
        return shapes

    def __dbfHeaderLength(self):
        """Retrieves the header length of a dbf file header."""
        if not self.__dbfHdrLength:
            if not self.dbf:
                raise ShapefileException("Shapefile Reader requires a shapefile or file-like object. (no dbf file found)")
            dbf = self.dbf
            (self.numRecords, self.__dbfHdrLength) = \
                    unpack("<xxxxLH22x", dbf.read(32))
        return self.__dbfHdrLength

    def __dbfHeader(self):
        """Reads a dbf header. Xbase-related code borrows heavily from ActiveState Python Cookbook Recipe 362715 by Raymond Hettinger"""
        if not self.dbf:
            raise ShapefileException("Shapefile Reader requires a shapefile or file-like object. (no dbf file found)")
        dbf = self.dbf
        headerLength = self.__dbfHeaderLength()
        numFields = (headerLength - 33) // 32
        for field in range(numFields):
            fieldDesc = list(unpack("<11sc4xBB14x", dbf.read(32)))
            name = 0
            idx = 0
            if b("\x00") in fieldDesc[name]:
                idx = fieldDesc[name].index(b("\x00"))
            else:
                idx = len(fieldDesc[name]) - 1
            fieldDesc[name] = fieldDesc[name][:idx]
            fieldDesc[name] = u(fieldDesc[name])
            fieldDesc[name] = fieldDesc[name].lstrip()
            fieldDesc[1] = u(fieldDesc[1])
            self.fields.append(fieldDesc)
        terminator = dbf.read(1)
        assert terminator == b("\r")
        self.fields.insert(0, ('DeletionFlag', 'C', 1, 0))

    def __recordFmt(self):
        """Calculates the size of a .shp geometry record."""
        if not self.numRecords:
            self.__dbfHeader()
        fmt = ''.join(['%ds' % fieldinfo[2] for fieldinfo in self.fields])
        fmtSize = calcsize(fmt)
        return (fmt, fmtSize)

    def __record(self):
        """Reads and returns a dbf record row as a list of values."""
        f = self.__getFileObj(self.dbf)
        recFmt = self.__recordFmt()
        recordContents = unpack(recFmt[0], f.read(recFmt[1]))
        if recordContents[0] != b(' '):
            # deleted record
            return None
        record = []
        for (name, typ, size, deci), value in zip(self.fields,
                                                                                                recordContents):
            if name == 'DeletionFlag':
                continue
            elif not value.strip():
                record.append(value)
                continue
            elif typ == "N":
                value = value.replace(b('\0'), b('')).strip()
                if value == b(''):
                    value = 0
                elif deci:
                    value = float(value)
                else:
                    value = int(value)
            elif typ == b('D'):
                try:
                    y, m, d = int(value[:4]), int(value[4:6]), int(value[6:8])
                    value = [y, m, d]
                except:
                    value = value.strip()
            elif typ == b('L'):
                value = (value in b('YyTt') and b('T')) or \
                                        (value in b('NnFf') and b('F')) or b('?')
            else:
                value = u(value)
                value = value.strip()
            record.append(value)
        return record

    def record(self, i=0):
        """Returns a specific dbf record based on the supplied index."""
        f = self.__getFileObj(self.dbf)
        if not self.numRecords:
            self.__dbfHeader()
        i = self.__restrictIndex(i)
        recSize = self.__recordFmt()[1]
        f.seek(0)
        f.seek(self.__dbfHeaderLength() + (i * recSize))
        return self.__record()

    def records(self):
        """Returns all records in a dbf file."""
        if not self.numRecords:
            self.__dbfHeader()
        records = []
        f = self.__getFileObj(self.dbf)
        f.seek(self.__dbfHeaderLength())
        for i in range(self.numRecords):
            r = self.__record()
            if r:
                records.append(r)
        return records

    def shapeRecord(self, i=0):
        """Returns a combination geometry and attribute record for the
        supplied record index."""
        i = self.__restrictIndex(i)
        return _ShapeRecord(shape=self.shape(i),
                                                        record=self.record(i))

    def shapeRecords(self):
        """Returns a list of combination geometry/attribute records for
        all records in a shapefile."""
        shapeRecords = []
        return [_ShapeRecord(shape=rec[0], record=rec[1]) \
                                for rec in zip(self.shapes(), self.records())]

class Writer:
    """Provides write support for ESRI Shapefiles."""
    def __init__(self, shapeType=None):
        self._shapes = []
        self.fields = []
        self.records = []
        self.shapeType = shapeType
        self.shp = None
        self.shx = None
        self.dbf = None
        # Geometry record offsets and lengths for writing shx file.
        self._offsets = []
        self._lengths = []
        # Use deletion flags in dbf? Default is false (0).
        self.deletionFlag = 0

    def __getFileObj(self, f):
        """Safety handler to verify file-like objects"""
        if not f:
            raise ShapefileException("No file-like object available.")
        elif hasattr(f, "write"):
            return f
        else:
            pth = os.path.split(f)[0]
            if pth and not os.path.exists(pth):
                os.makedirs(pth)
            return open(f, "wb")

    def __shpFileLength(self):
        """Calculates the file length of the shp file."""
        # Start with header length
        size = 100
        # Calculate size of all shapes
        for s in self._shapes:
            # Add in record header and shape type fields
            size += 12
            # nParts and nPoints do not apply to all shapes
            #if self.shapeType not in (0,1):
            #       nParts = len(s.parts)
            #       nPoints = len(s.points)
            if hasattr(s,'parts'):
                nParts = len(s.parts)
            if hasattr(s,'points'):
                nPoints = len(s.points)
            # All shape types capable of having a bounding box
            if self.shapeType in (3,5,8,13,15,18,23,25,28,31):
                size += 32
            # Shape types with parts
            if self.shapeType in (3,5,13,15,23,25,31):
                # Parts count
                size += 4
                # Parts index array
                size += nParts * 4
            # Shape types with points
            if self.shapeType in (3,5,8,13,15,23,25,31):
                # Points count
                size += 4
                # Points array
                size += 16 * nPoints
            # Calc size of part types for Multipatch (31)
            if self.shapeType == 31:
                size += nParts * 4
            # Calc z extremes and values
            if self.shapeType in (13,15,18,31):
                # z extremes
                size += 16
                # z array
                size += 8 * nPoints
            # Calc m extremes and values
            if self.shapeType in (23,25,31):
                # m extremes
                size += 16
                # m array
                size += 8 * nPoints
            # Calc a single point
            if self.shapeType in (1,11,21):
                size += 16
            # Calc a single Z value
            if self.shapeType == 11:
                size += 8
            # Calc a single M value
            if self.shapeType in (11,21):
                size += 8
        # Calculate size as 16-bit words
        size //= 2
        return size

    def __bbox(self, shapes, shapeTypes=[]):
        x = []
        y = []
        for s in shapes:
            shapeType = self.shapeType
            if shapeTypes:
                shapeType = shapeTypes[shapes.index(s)]
            px, py = list(zip(*s.points))[:2]
            x.extend(px)
            y.extend(py)
        return [min(x), min(y), max(x), max(y)]

    def __zbox(self, shapes, shapeTypes=[]):
        z = []
        for s in shapes:
            try:
                for p in s.points:
                    z.append(p[2])
            except IndexError:
                pass
        if not z: z.append(0)
        return [min(z), max(z)]

    def __mbox(self, shapes, shapeTypes=[]):
        m = [0]
        for s in shapes:
            try:
                for p in s.points:
                    m.append(p[3])
            except IndexError:
                pass
        return [min(m), max(m)]

    def bbox(self):
        """Returns the current bounding box for the shapefile which is
        the lower-left and upper-right corners. It does not contain the
        elevation or measure extremes."""
        return self.__bbox(self._shapes)

    def zbox(self):
        """Returns the current z extremes for the shapefile."""
        return self.__zbox(self._shapes)

    def mbox(self):
        """Returns the current m extremes for the shapefile."""
        return self.__mbox(self._shapes)

    def __shapefileHeader(self, fileObj, headerType='shp'):
        """Writes the specified header type to the specified file-like object.
        Several of the shapefile formats are so similar that a single generic
        method to read or write them is warranted."""
        f = self.__getFileObj(fileObj)
        f.seek(0)
        # File code, Unused bytes
        f.write(pack(">6i", 9994,0,0,0,0,0))
        # File length (Bytes / 2 = 16-bit words)
        if headerType == 'shp':
            f.write(pack(">i", self.__shpFileLength()))
        elif headerType == 'shx':
            f.write(pack('>i', ((100 + (len(self._shapes) * 8)) // 2)))
        # Version, Shape type
        f.write(pack("<2i", 1000, self.shapeType))
        # The shapefile's bounding box (lower left, upper right)
        if self.shapeType != 0:
            try:
                f.write(pack("<4d", *self.bbox()))
            except error:
                raise ShapefileException("Failed to write shapefile bounding box. Floats required.")
        else:
            f.write(pack("<4d", 0,0,0,0))
        # Elevation
        z = self.zbox()
        # Measure
        m = self.mbox()
        try:
            f.write(pack("<4d", z[0], z[1], m[0], m[1]))
        except error:
            raise ShapefileException("Failed to write shapefile elevation and measure values. Floats required.")

    def __dbfHeader(self):
        """Writes the dbf header and field descriptors."""
        f = self.__getFileObj(self.dbf)
        f.seek(0)
        version = 3
        year, month, day = time.localtime()[:3]
        year -= 1900
        # Remove deletion flag placeholder from fields
        for field in self.fields:
            if field[0].startswith("Deletion"):
                self.fields.remove(field)
        numRecs = len(self.records)
        numFields = len(self.fields)
        headerLength = numFields * 32 + 33
        recordLength = sum([int(field[2]) for field in self.fields]) + 1
        header = pack('<BBBBLHH20x', version, year, month, day, numRecs,
                headerLength, recordLength)
        f.write(header)
        # Field descriptors
        for field in self.fields:
            name, fieldType, size, decimal = field
            name = b(name)
            name = name.replace(b(' '), b('_'))
            name = name.ljust(11).replace(b(' '), b('\x00'))
            fieldType = b(fieldType)
            size = int(size)
            fld = pack('<11sc4xBB14x', name, fieldType, size, decimal)
            f.write(fld)
        # Terminator
        f.write(b('\r'))

    def __shpRecords(self):
        """Write the shp records"""
        f = self.__getFileObj(self.shp)
        f.seek(100)
        recNum = 1
        for s in self._shapes:
            self._offsets.append(f.tell())
            # Record number, Content length place holder
            f.write(pack(">2i", recNum, 0))
            recNum += 1
            start = f.tell()
            # Shape Type
            f.write(pack("<i", s.shapeType))
            # All shape types capable of having a bounding box
            if s.shapeType in (3,5,8,13,15,18,23,25,28,31):
                try:
                    f.write(pack("<4d", *self.__bbox([s])))
                except error:
                    raise ShapefileException("Falied to write bounding box for record %s. Expected floats." % recNum)
            # Shape types with parts
            if s.shapeType in (3,5,13,15,23,25,31):
                # Number of parts
                f.write(pack("<i", len(s.parts)))
            # Shape types with multiple points per record
            if s.shapeType in (3,5,8,13,15,23,25,31):
                # Number of points
                f.write(pack("<i", len(s.points)))
            # Write part indexes
            if s.shapeType in (3,5,13,15,23,25,31):
                for p in s.parts:
                    f.write(pack("<i", p))
            # Part types for Multipatch (31)
            if s.shapeType == 31:
                for pt in s.partTypes:
                    f.write(pack("<i", pt))
            # Write points for multiple-point records
            if s.shapeType in (3,5,8,13,15,23,25,31):
                try:
                    [f.write(pack("<2d", *p[:2])) for p in s.points]
                except error:
                    raise ShapefileException("Failed to write points for record %s. Expected floats." % recNum)
            # Write z extremes and values
            if s.shapeType in (13,15,18,31):
                try:
                    f.write(pack("<2d", *self.__zbox([s])))
                except error:
                    raise ShapefileException("Failed to write elevation extremes for record %s. Expected floats." % recNum)
                try:
                    [f.write(pack("<d", p[2])) for p in s.points]
                except error:
                    raise ShapefileException("Failed to write elevation values for record %s. Expected floats." % recNum)
            # Write m extremes and values
            if s.shapeType in (23,25,31):
                try:
                    f.write(pack("<2d", *self.__mbox([s])))
                except error:
                    raise ShapefileException("Failed to write measure extremes for record %s. Expected floats" % recNum)
                try:
                    [f.write(pack("<d", p[3])) for p in s.points]
                except error:
                    raise ShapefileException("Failed to write measure values for record %s. Expected floats" % recNum)
            # Write a single point
            if s.shapeType in (1,11,21):
                try:
                    f.write(pack("<2d", s.points[0][0], s.points[0][1]))
                except error:
                    raise ShapefileException("Failed to write point for record %s. Expected floats." % recNum)
            # Write a single Z value
            if s.shapeType == 11:
                try:
                    f.write(pack("<1d", s.points[0][2]))
                except error:
                    raise ShapefileException("Failed to write elevation value for record %s. Expected floats." % recNum)
            # Write a single M value
            if s.shapeType in (11,21):
                try:
                    f.write(pack("<1d", s.points[0][3]))
                except error:
                    raise ShapefileException("Failed to write measure value for record %s. Expected floats." % recNum)
            # Finalize record length as 16-bit words
            finish = f.tell()
            length = (finish - start) // 2
            self._lengths.append(length)
            # start - 4 bytes is the content length field
            f.seek(start-4)
            f.write(pack(">i", length))
            f.seek(finish)

    def __shxRecords(self):
        """Writes the shx records."""
        f = self.__getFileObj(self.shx)
        f.seek(100)
        for i in range(len(self._shapes)):
            f.write(pack(">i", self._offsets[i] // 2))
            f.write(pack(">i", self._lengths[i]))

    def __dbfRecords(self):
        """Writes the dbf records."""
        f = self.__getFileObj(self.dbf)
        for record in self.records:
            if not self.fields[0][0].startswith("Deletion"):
                f.write(b(' ')) # deletion flag
            for (fieldName, fieldType, size, dec), value in zip(self.fields, record):
                fieldType = fieldType.upper()
                size = int(size)
                if fieldType.upper() == "N":
                    value = str(value).rjust(size)
                elif fieldType == 'L':
                    value = str(value)[0].upper()
                else:
                    value = str(value)[:size].ljust(size)
                assert len(value) == size
                value = b(value)
                f.write(value)

    def null(self):
        """Creates a null shape."""
        self._shapes.append(_Shape(NULL))

    def point(self, x, y, z=0, m=0):
        """Creates a point shape."""
        pointShape = _Shape(self.shapeType)
        pointShape.points.append([x, y, z, m])
        self._shapes.append(pointShape)

    def line(self, parts=[], shapeType=POLYLINE):
        """Creates a line shape. This method is just a convienience method
        which wraps 'poly()'.
        """
        self.poly(parts, shapeType, [])

    def poly(self, parts=[], shapeType=POLYGON, partTypes=[]):
        """Creates a shape that has multiple collections of points (parts)
        including lines, polygons, and even multipoint shapes. If no shape type
        is specified it defaults to 'polygon'. If no part types are specified
        (which they normally won't be) then all parts default to the shape type.
        """
        polyShape = _Shape(shapeType)
        polyShape.parts = []
        polyShape.points = []
        for part in parts:
            polyShape.parts.append(len(polyShape.points))
            for point in part:
                # Ensure point is list
                if not isinstance(point, list):
                    point = list(point)
                # Make sure point has z and m values
                while len(point) < 4:
                    point.append(0)
                polyShape.points.append(point)
        if polyShape.shapeType == 31:
            if not partTypes:
                for part in parts:
                    partTypes.append(polyShape.shapeType)
            polyShape.partTypes = partTypes
        self._shapes.append(polyShape)

    def field(self, name, fieldType="C", size="50", decimal=0):
        """Adds a dbf field descriptor to the shapefile."""
        self.fields.append((name, fieldType, size, decimal))

    def record(self, *recordList, **recordDict):
        """Creates a dbf attribute record. You can submit either a sequence of
        field values or keyword arguments of field names and values. Before
        adding records you must add fields for the record values using the
        fields() method. If the record values exceed the number of fields the
        extra ones won't be added. In the case of using keyword arguments to specify
        field/value pairs only fields matching the already registered fields
        will be added."""
        record = []
        fieldCount = len(self.fields)
        # Compensate for deletion flag
        if self.fields[0][0].startswith("Deletion"): fieldCount -= 1
        if recordList:
            [record.append(recordList[i]) for i in range(fieldCount)]
        elif recordDict:
            for field in self.fields:
                if field[0] in recordDict:
                    val = recordDict[field[0]]
                    if val:
                        record.append(val)
                    else:
                        record.append("")
        if record:
            self.records.append(record)

    def shape(self, i):
        return self._shapes[i]

    def shapes(self):
        """Return the current list of shapes."""
        return self._shapes

    def saveShp(self, target):
        """Save an shp file."""
        if not hasattr(target, "write"):
            target = os.path.splitext(target)[0] + '.shp'
        if not self.shapeType:
            self.shapeType = self._shapes[0].shapeType
        self.shp = self.__getFileObj(target)
        self.__shapefileHeader(self.shp, headerType='shp')
        self.__shpRecords()

    def saveShx(self, target):
        """Save an shx file."""
        if not hasattr(target, "write"):
            target = os.path.splitext(target)[0] + '.shx'
        if not self.shapeType:
            self.shapeType = self._shapes[0].shapeType
        self.shx = self.__getFileObj(target)
        self.__shapefileHeader(self.shx, headerType='shx')
        self.__shxRecords()

    def saveDbf(self, target):
        """Save a dbf file."""
        if not hasattr(target, "write"):
            target = os.path.splitext(target)[0] + '.dbf'
        self.dbf = self.__getFileObj(target)
        self.__dbfHeader()
        self.__dbfRecords()

    def save(self, target=None, shp=None, shx=None, dbf=None):
        """Save the shapefile data to three files or
        three file-like objects. SHP and DBF files can also
        be written exclusively using saveShp, saveShx, and saveDbf respectively."""
        # TODO: Create a unique filename for target if None.
        if shp:
            self.saveShp(shp)
        if shx:
            self.saveShx(shx)
        if dbf:
            self.saveDbf(dbf)
        elif target:
            self.saveShp(target)
            self.shp.close()
            self.saveShx(target)
            self.shx.close()
            self.saveDbf(target)
            self.dbf.close()

class Editor(Writer):
    def __init__(self, shapefile=None, shapeType=POINT, autoBalance=1):
        self.autoBalance = autoBalance
        if not shapefile:
            Writer.__init__(self, shapeType)
        elif is_string(shapefile):
            base = os.path.splitext(shapefile)[0]
            if os.path.isfile("%s.shp" % base):
                r = Reader(base)
                Writer.__init__(self, r.shapeType)
                self._shapes = r.shapes()
                self.fields = r.fields
                self.records = r.records()

    def select(self, expr):
        """Select one or more shapes (to be implemented)"""
        # TODO: Implement expressions to select shapes.
        pass

    def delete(self, shape=None, part=None, point=None):
        """Deletes the specified part of any shape by specifying a shape
        number, part number, or point number."""
        # shape, part, point
        if shape and part and point:
            del self._shapes[shape][part][point]
        # shape, part
        elif shape and part and not point:
            del self._shapes[shape][part]
        # shape
        elif shape and not part and not point:
            del self._shapes[shape]
        # point
        elif not shape and not part and point:
            for s in self._shapes:
                if s.shapeType == 1:
                    del self._shapes[point]
                else:
                    for part in s.parts:
                        del s[part][point]
        # part, point
        elif not shape and part and point:
            for s in self._shapes:
                del s[part][point]
        # part
        elif not shape and part and not point:
            for s in self._shapes:
                del s[part]

    def point(self, x=None, y=None, z=None, m=None, shape=None, part=None, point=None, addr=None):
        """Creates/updates a point shape. The arguments allows
        you to update a specific point by shape, part, point of any
        shape type."""
        # shape, part, point
        if shape and part and point:
            try: self._shapes[shape]
            except IndexError: self._shapes.append([])
            try: self._shapes[shape][part]
            except IndexError: self._shapes[shape].append([])
            try: self._shapes[shape][part][point]
            except IndexError: self._shapes[shape][part].append([])
            p = self._shapes[shape][part][point]
            if x: p[0] = x
            if y: p[1] = y
            if z: p[2] = z
            if m: p[3] = m
            self._shapes[shape][part][point] = p
        # shape, part
        elif shape and part and not point:
            try: self._shapes[shape]
            except IndexError: self._shapes.append([])
            try: self._shapes[shape][part]
            except IndexError: self._shapes[shape].append([])
            points = self._shapes[shape][part]
            for i in range(len(points)):
                p = points[i]
                if x: p[0] = x
                if y: p[1] = y
                if z: p[2] = z
                if m: p[3] = m
                self._shapes[shape][part][i] = p
        # shape
        elif shape and not part and not point:
            try: self._shapes[shape]
            except IndexError: self._shapes.append([])

        # point
        # part
        if addr:
            shape, part, point = addr
            self._shapes[shape][part][point] = [x, y, z, m]
        else:
            Writer.point(self, x, y, z, m)
        if self.autoBalance:
            self.balance()

    def validate(self):
        """An optional method to try and validate the shapefile
        as much as possible before writing it (not implemented)."""
        #TODO: Implement validation method
        pass

    def balance(self):
        """Adds a corresponding empty attribute or null geometry record depending
        on which type of record was created to make sure all three files
        are in synch."""
        if len(self.records) > len(self._shapes):
            self.null()
        elif len(self.records) < len(self._shapes):
            self.record()

    def __fieldNorm(self, fieldName):
        """Normalizes a dbf field name to fit within the spec and the
        expectations of certain ESRI software."""
        if len(fieldName) > 11: fieldName = fieldName[:11]
        fieldName = fieldName.upper()
        fieldName.replace(' ', '_')

# Begin Testing
def test():
    import doctest
    doctest.NORMALIZE_WHITESPACE = 1
    doctest.testfile("README.txt", verbose=1)

if __name__ == "__main__":
    """
    Doctests are contained in the module 'pyshp_usage.py'. This library was developed
    using Python 2.3. Python 2.4 and above have some excellent improvements in the built-in
    testing libraries but for now unit testing is done using what's available in
    2.3.
    """
    test()

App开源项目收集和汇总:App Store地址和源代码

$
0
0

这篇文章的内容来源于论坛2010的一篇帖子,最初列举了23个开源App的App Store地址和源代码,删除了不能下载源码的app,也添加了一些。由于源码托管的地方不一样,所以只下载了github上的部分源码,一些比较大的源码没有上传,所以烦请各位自己下载了。

非常感谢无私分享自己成果的开发者,为那些行业新进入者提供了很好的学习范例。

为了给大家提供更多方便,我们现在计划收集更多的App开源项目,如果你有压箱底儿的宝贝,赶快拿出来晒一晒吧!或者你在app中使用了哪些开源项目,欢迎向我们推荐!可附上你的app下载地址。我们的宗旨是一起学习,一起进步,一起提高技术水平!

1. Colloquy –  (itunes link) (source code)

2. Diceshaker -  (itunes link) (source code)   附件:

/cms/uploads/soft/130523/4196-1305231Q114.zip

 

3. Doom Classic  (itunes link) (source code) (build instructions)

5. Mobilesynth  (itunes link) (source code)
6. Molecules – (itunes link) (source code)
8. NowPlaying –  (itunes link) (source code)    (有源码,itunes 链接已失效)

9. Packlog –  (itunes link) (source code)            (有源码,itunes 链接已失效)

10. PocketFlix –  (itunes link) (source code)       (有源码,itunes 链接已失效)

11. Sci-15 HPCalc –  (itunes link) (source code)   (有源码,itunes 链接已失效)

12. Tweejump – (itunes link) (source code)   附件:

/cms/uploads/soft/130523/4196-1305231Q933.zip

 

13. Tweetero – (itunes link) (source code)
14. WordPress –  (itunes link) (source code)
15. YourRights –  (itunes link) (source code)
18.AntiMapLog–  (itunes link) (source code)
19.AnyPic–  (itunes link) (source code)  附件偏大,自己动手哈!
20.AppSlate–  (itunes link) (source code)   附件偏大,自己动手!
23.Barcode Scanner –  (itunes link) (source code)
25.Canabalt–  (itunes link) (source code)   附件偏大,自己动手哈!!
29.Climbers–  (itunes link) (source code)    
30.ComicFlow–  (itunes link) (source code)   
等你来分享!!

 App开源项目收集和汇总:App Store地址和源代码(一),再次收集整理了20个App开源项目.

1. Desktop Web Analytics Mobile(itunes link) (source code)  附件:

/cms/uploads/soft/130524/4196-130524102Z1.zip

 

2. Diceshaker—(itunes link) (source code)   附件:

/cms/uploads/soft/130524/4196-130524103139.zip

 

4. Edhita(iPad文本编辑器)–(itunes link) (source code)   附件:

/cms/uploads/soft/130524/4673-130524104958.zip

 

5. Fresh Food Finder(定位农贸市场,找到新鲜食物)–(itunes link) (source code)   附件:

/cms/uploads/soft/130524/4673-130524105202.zip

 

6. Frotz–(itunes link) (source code)

7. Pain Guide–(itunes link) (source code)

8. Gorillas Lite–(itunes link) (source code)
9. Greek Interlinear Bible–(itunes link) (source code)
10. Heredox–(itunes link) (source code)
11. HoxChess–(itunes link) (source code)
12. iLabyrinth(Cocos2D写的益智游戏)–(itunes link) (source code)
13. iStrobe – Flash & Strobe Light for iPhone 4–(itunes link) (source code)
15. Little Go–(itunes link) (source code)

16. MapBox Earth–(itunes link) (source code)    附件:

/cms/uploads/soft/130524/4673-130524113047.zip

 

17.Master Password(储存密码)–(itunes link) (source code)

18. Mugician(音乐合成器)–(itunes link) (source code)   附件:

/cms/uploads/soft/130524/4673-130524113619.zip

 

19. NatsuLion for iPhone/iPod touch(Twitter客户端)–(itunes link) (source code)    附件:

/cms/uploads/soft/130524/4673-130524114049.zip

 

20.Next Airport(导航和天气)–(itunes link) (source code)   附件:

/cms/uploads/soft/130524/4673-130524114355.zip

 

 

 


PGMN and PWQMN test cases

$
0
0

PGMN test case: search W0000036-1 and make sure the Chemistry tab contain the sample date of 31/10/2011.
search W0000001-1 and make sure the water level chart has the data between 2011 and 2012.
search 39 and make sure the precipitation chart has the data between 2011 and 2012.

Modify the maxYearCoordinate to 2012.


why parseInt(’08′) is giving 0, whereas parseInt(’07′) is giving 7

$
0
0

I am working on javascript, and I seem to find this strange, that the javascript function parseInt(’08′) is returning 0 and parseInt(’07′) is returning 7.

this behavior seems to be there in Firefox.

parseInt(’08′) is returning 8 in IE, but 0 in Firefox..

Why? I want parseInt(’08′) to return 8, as expected and getting in IE.

 

Yeah, I came across this one before. It is really odd because some browsers interpret this as you wanting to parse it in base 8. Consider the following article:

http://www.breakingpar.com/bkp/home.nsf/0/87256B280015193F87256C85006A6604

basically, you have to tell the parser to use base 10 numbers:

parseInt('08', '10');

 



Batch Geonews: Stamen Map Stack, 1,000 New Street View, Protest Maptivism, D3.js Geo, 270TB of Bird’s Eye, and much more

$
0
0

Batch Geonews: Stamen Map Stack, 1,000 New Street View, Protest Maptivism, D3.js Geo, 270TB of Bird’s Eye, and much more

Fri, 2013/06/14 – 08:31 — Satri

Here’s the recent geonews in batch mode.

From the open source / open data front:

From the Esri front:

From the Google front:

In the miscellaneous category:

In the maps category:


QT + OpenCV

$
0
0

QT Console

 

#include <opencv2/opencv.hpp>
//#include <opencv2/highgui/highgui.hpp>

int main(int argc, char *argv[])
{
    cv::Mat image = cv::imread("img.jpg");
            cv::namedWindow("My Image");
    cv::imshow("My Image", image);
    cv::waitKey(5000);
    return 1;
}

pro file

#-------------------------------------------------
#
# Project created by QtCreator 2013-06-17T14:15:10
#
#-------------------------------------------------

QT       += core

QT       -= gui

TARGET = myQTConsoleProject
CONFIG   += console
CONFIG   -= app_bundle

TEMPLATE = app


SOURCES += main.cpp

INCLUDEPATH += ~/opencv/opencv-2.4.5/include/
LIBS += -L/usr/local/lib \
-lopencv_core \
-lopencv_highgui \
-lopencv_imgproc \
-lopencv_features2d \
-lopencv_calib3d

 

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QFileDialog>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_pushButton_clicked()
{
    QString fileName = QFileDialog::getOpenFileName(this,
        tr("Open Image"), ".",
        tr("Image Files (*.png *.jpg *.jpeg *.bmp)"));
    image= cv::imread(fileName.toLatin1().data());
    cv::namedWindow("Original Image");
    cv::imshow("Original Image", image);
}

void MainWindow::on_pushButton_2_clicked()
{
    cv::flip(image,image,1);
    cv::namedWindow("Output Image");
    cv::imshow("Output Image", image);
}

Calculate Primes

$
0
0
// Fibonacci
// http://en.wikipedia.org/wiki/Fibonacci_number
var isPrime = function(n, arr) {
	for (var i = 0; i < arr.length; i++) {
		if (arr[i] > Math.sqrt(n)) {
			break;
		}
		if ((n % arr[i]) == 0) {
			return false;
		}
	}
	return true;
};

 // Fibonacci: closed form expression
 // http://en.wikipedia.org/wiki/Golden_ratio#Relationship_to_Fibonacci_sequence
var calculatePrime = function(arr) {
	if(arr.length == 0) {
		return [2];
	}
	var i = arr[arr.length - 1] + 1;
	while (!isPrime(i, arr)) {
		i = i + 1;
	}
	arr.push(i)
	return arr;
};

// Find first K Fibonacci numbers via basic for loop
var firstPrimeNumber = function(k) {
	var i = 1;
	var arr = [];
	while (arr.length < k) {
		arr = calculatePrime(arr);
	}
	return arr;
};

 // Print to console
 var fmt = function(arr) {
	return arr.join(",");
	//return arr;
 };

 var k = 100;
 //console.log("firstkfib(" + k + ")");
 //console.log(fmt(firstPrimeNumber(k)));
 var fs = require('fs');
 var outfile = "prime.txt";
 var out = fmt(firstPrimeNumber(k));
 fs.writeFileSync(outfile, out);  
 //console.log("Script: " + __filename + "\nWrote: " + out + "To: " + outfile);


AWS, heroKu Set up

$
0
0

$ cd ~
$ cp /cygdrive/c/Users/JohnSmith/Downloads/cs184-john-stanford-edu.pem .
$ chgrp Users cs184-john-stanford-edu
$ chmod 400 cs184-john-stanford-edu.pem
$ ssh -i cs184-john-stanford-edu.pem \
ubuntu@ec2-50-19-140-229.compute-1.amazonaws.com

# 1) install heroku and git
$ sudo apt-get install -y git-core
wget -qO- https://toolbelt.heroku.com/install-ubuntu.sh | sh
$ which git
$ which heroku
# 2) Login and set up your SSH keys
$ heroku login
$ ssh-keygen -t rsa
$ heroku keys:add
# 3) Clone a sample repo and push to heroku
$ git clone https://github.com/heroku/node-js-sample.git
$ cd node-js-sample
$ heroku create
$ git push heroku master


The Battle of Modern Javascript Frameworks

$
0
0

The Battle of Modern Javascript Frameworks – Part I

Posted April 10, 2013 by Bradley Trager and Roman Kagan & filed under Javascript Libraries.

Welcome to the Javascript Framework revolution.  Since the release of JQuery in 2006, client-side javascript has entered a renaissance, and many developers have decided to shift much of the functionality of there applications to the client side, while using the server primarily to send and recieve data.  Shifting functionality to the client side enables the potential for a much more powerful and responsive UI, which has always been an advantage of native apps. But with JQuery alone it still takes a great deal of effort to create a web app with the unparalled feel of a native app. Now everyone is wondering what will be the next paradigm in client-side development, and more importantly which framework they should choose for their next application. There are over 20 mainstream frameworks that operate on top (or next to) JQuery for developers to choose from (seeTodo MVC Project). No to mention several smaller more focused libraries.

This article will be the first in a series of posts that will hopefully shed light one several major frameworks in order to give you a picture of the advantages and disadvantages of each one, and help you to choose which one is right for your next project.  But before we talk about any specific frameworks, we will first outline some of the major features that these types of frameworks offer, and then we will outline our requirements in evaluating different frameworks.

Overview

In the words of Knockout.js creator Steve Sanderson “All the technologies follow from the view that serious JavaScript applications require proper data models and ability to do client-side rendering, not just server rendering plus some Ajax and jQuery code.” All the frameworks that we will discuss implement their data model as part of an MVC type architecture or some variation of MVC, and most have some sort of built in templating. A data model enables features like dependency tracking, and data-binding as well as the idea of “state” within a web app. Data models also make it much easier to sync data with the server. Templating enables applications to programmatically render views that are modular and reusable. As we evaluate each framework, we will look at how it implements these features and what additional features it offers that come as a result of the data model and templating.

Requirements

1.                Features

What are the main features the framework offers? And how do they meet the needs of the project?

2.                Strength of the Project and Community

Who are the core developers? How many people are active in the community? How well documented is the framework?

3.                Ease of Learning

How easy is the framework for developers to learn and use?

4.                Compatibility and Extensibility

Is the framework compatible with 3rd party libraries? Is it easily extensible?

5.                Testing and Debugging?

Are there any tools for testing and debugging? What are common debuging methods?

6.                Pros and Cons

Part II – Knockout.js

Knockout is the simplest of all the frameworks as well as one of the smallest. It does not claim to be able to solve all your problems, but it does claim to do a few useful things (see “features” below) and do it better than the alternatives. In the words of Knockout creator Steve Sanderson, it is “low risk” because its uses are focused and it is compatible with any other technologies you are using and you can use it in a very small part of your application or your entire application. For someone who wants to take their project to the next level without too much risk, knockout is a great choice.

Features

-Model-View-ViewModel architecture
-Tracks changes in your data by wrapping all your variables in an “observable function” creating a dependency graph which automatically controls the flow of events when something changes in your application.
-DOM or String-Based Templating for modularizing view and rendering them programmatically

Strength of the Project and Community

The Knockout project was released by Steve Sanderson (who is now on the Microsoft ASP.NET team) back in 2010 before he came to Microsoft. Since then a second version has been released, there have been a number of plugins written for it, Microsoft has incorporated intellisense support for it into Visual Studio 2012, and developers Michael Best and Ryan Niemeyer have joined the core Knockout.js development team. As a rough estimate of the size of the community, there are currently 3,533 people who have starred the KO GitHub repository, and 5,055 stack-overflow questions with knockout tags.

Ease of Learning

The documentation for knockout is probably the best we have ever seen. It has an amazing interactive tutorial system, live examples, and the documentation is extremely comprehensive and easy to understand. For most developers already familiar with javascript it should not take more than a few days to get up and running.

Compatibility and Extensibility

Knockout is compatible with any back-end technology and on the front-end it does as much or as little as you decide. Knockout purposely leaves many decisions to the developer to decide letting developers pick the best tool to meet their specific needs. Knockout has no opinion about how you communicate with the server or how you choose to do routing, but there are several third party libraries that do a fine job of these things (like sammy.js for routing and upshot.js for data-access). Knockout is one of the only frameworks that actually offers support for IE 6 if that is something you are interested in. It goes without mentioning that it works fine next to JQuery. Knockout is easily extensible. To modify or extend the default functionality, Knockout offers a features like custom bindings, and custom functions. As mentioned above, there are also several plugins already written for knockout.

Testing and Debugging

As of now, we have not seen any specific debugging tools for knockout, but it works fine with Javascript testing tools such as jasmine. Since knockout uses dependency injection, it is well suited for unit testing. Console.log() is handy, and knockout also offers a function ko.toJSON() which converts a KO view model to JSON and can be bound to a text area somewhere on the page so that developers can see in real time how their data is changing. For testing and prototyping, we find that it is easy to use implement knockout applications in jsfiddle.

Pros

-Highly compatible with other 3rd party js libraries
-Easy to learn and use
-Dependencies are handled through a dependency graph which targets specific data as opposed to updating entire models when data changes like in Angular.js. This may increase performance in data-heavy applications compared to “dirt checking” which Angular uses (but see this post on Stack Overflow which defends Angular’s approach).

Cons

-All javascript variables and arrays are functions (aka KO observables) as opposed to angular which can be a little confusing at first for some people. But it should be noted that all native javascript array functions like splice(), indexOf(), push() etc. are implemented by Knockout and may be used on KO observables.
-HTML views can get messy as declarative bindings increase. This can be mitigated to some extent through the use of custom bindings, computed observables and by attaching events to the DOM using JQuery (see Unobtrusive Event Handling) instead of using the data-bind attribute.
-Additional functionality like data-access and url-routing are not included. If you are looking for an end to end solution that offers a complete toolbox of common web-app functionality, you should probably check out frameworks like Angular or Ember.

Part III: Backbone.js

Right now Backbone.js is probably the most popular as well as simplest of the modern javascript frameworks. As its name describes, it main goal is to give a nice MVC structure to your javascript code. Backbones goal is to clean up your code by organizing all your data and event handlers into javascript objects. This way, events can be triggered whenever your data model changes thereby avoiding a lot of messy JQuery selectors and event bindings. It also has nice features for accessing data through RESTful APIs and URL routing. Other than that, Backbone tries to leave as much as possible up to the developer.

Features

-MVC style architecture for organizing your code
-Models and collections which can be persisted through a RESTful API
-Views which can automatically update by listening for changes in your model
-An optional routing system to define states of your app for single page applications

Strength of the Project and Community

Backbone.js has been in development since 2010 and in March 2013 just released version 1.0. It has one of the largest community of all the Javascript frameworks with rough indicators of 13,623 stars on GitHub, and 8,209 questions on stack-overflow. Many well known projects are using Backbone in their apps like DocumentCloud, LinkedIn Mobile, Walmart Mobile, Groupon, and CodeSchool just to name a few.

Ease of Learning

On one hand, backbone is a relatively simple library. If you go to their website, you will find a link to the annotated source code, which shouldn’t be hard for the average developer to read and understand. On the other hand, backbone has several concepts (e.i. model, collection, view and router), and the documentation is not as extensive as knockout or angular for example. Also, inexperienced programmers could quickly get into trouble if they are not aware of proper conventions when using backbone.js. Backbone also relies heavily on underscore.js, so it is necessary to learn underscore first or pick it up along the way. From our own experience and hearing other stories, it takes longer to get up and running with backbone in comparison to knockout. On the other hand, even though the concepts in backbone may be difficult to grasp at first, once you get it you will not need so much time to discover the rest of its functionality.

Compatibility and Extensibility

Backbone like knockout does not force you to do anything in a particular way. It is great for those who prefer to make their own decisions on the lower level. It basically just gives you a nice MVC type structure for your javascript. Yes, it offers solutions for routing and data-access, but if you prefer to do those things differently, it is up to you. It can work in a small part of your application as well as the entire application.

Testing and Debugging

There seem to be some plugins written to help debugging, but we have not used them so we cannot comment on how useful they are. We have seen that people using Jasmine to run unit tests in backbone. In general, we find that backbone can be relatively difficult at times to debug.

Pros

-Relatively mature, proven framework with a strong community behind it
-Many extensions and scaffolding tools available
-Flexible, works fine for a new project or improving an existing one

Cons

-Lacks data-binding
-Requires a large amount of boiler-plate code
-Could be dangerous if programmers do not follow proper conventions

Part IV: Angular.js

Angular.js is the first framework that officially calls itslef a framework as opposed to knockout and backbone which actually refer to themselves as libraries. Angular claims to be great even for small local applications, but it is probably better suited as a foundation to base a whole new project on. Angular seeks to combine a complete toolbox for building dynamic single page web apps. It is relatively fast to produce applications, and requires less boilerplate code in comparison to backbone.js. As co-creator, Brad Green claims that at google, angular was used to write an application in three weeks that previously took 6 months, and since then many have had similar experiences.

Features

-Data binding (Uses “Dirty Checking” instead of Change Listeners. See this post on Stack Overflow.)
-Dependency injection
-DOM-based templating
-Routing and Deep-Linking
-Model-View-”Whatever” architecture
-Built it form validation
-Data persistence methods that work with RESTful APIs
-Web Components or “Directives” (Define your own HTML syntax)

Testing and Debugging

This is one area where Angular is outstanding. Angular comes packaged with built in testing tools, and there is a list of other tools available. Batarang is an extension for chrome that monitors your app’s model and measures your app’s performance. As we find by the rest of the frameworks, jasmine works, but there is also a plugin testacular (spectacular test runner) which allows you to run jasmine test on multiple browsers. Yet another extension available is Angular Scenario Runner which automates testing by simulating user interactions. Finally, since Angular uses dependency injection, it makes it ideal for unit testing.

Strength of the Project and Community

Angular was initially developed internally by Google, and now that it has been open sourced, Google continues to support its development. Angular has 8,523 Stars on GitHub, and 4,239 questions on stack overflow.

Ease of Learning

Angular is certainly more complex than knockout and backbone. It surely requires a bigger time investment. With that said, we feel that getting started with Angular is rather easy especially if you are familiar with knockout since the data-binding features are used in a very similar way. Angular also has comprehensive and well organized tutorials with plenty of examples on there website.

Compatibility and Extensibility

As far as compatibility goes, Angular can function in a particular part of your application so it does not get in the way of other frameworks you may be using. Angular is more abstract than knockout and backbone, it makes more decisions for you behind the scenes so that you can write less code, but this inevitably makes it harder to customize on a lower level. On a higher level, Angular gives the developer an API with extensive options.

Pros

-Uses primitive javascript types instead of function wrappers (“dirty checking”)
-Loaded with functionality
-Easy to get started
-Fast development and smaller amounts of boiler-plate code compared to backbone
-Makes testing easy and offers many testing tools
-Working with standards bodies to make browsers operate in the same way as angular

Cons

-Takes longer to learn than knockout and backbone
-Has not been proven in as many mainstream projects compared to backbone.js

Part V – Ember.js

Ember as it is described by its creators is ambitious and opinionated. The goal of developing ember is simple; create a web framework that will enable web apps to rival native apps. To accomplish this, ember offers an end to end solution with all the features you need to create a single page web app functioning in unison. Ember also seeks to abstract as much as possible from the programmer in order to take care of all the small decisions that programmers would otherwise have to make. Therefore, it is also opinionated about how certain things should be done including how you name your objects, and how you organize your files. Ember is also ambitious in the sense that one should ideally use ember for “ambitious” projects where the goal is to produce an outstanding web app that will have the feel of a native app.

Features

-Sophisticated handlebars templates
-Views that respond to events
-Routing for representing the state of the model and view in your application and enabling deep linking to return to a particular state
-Models that are persistable with a REST api
-Controllers which produce computed properties from the model and pass it to a view
-API for performing operations on Enumerable objects

Strength of the Project and Community

Ember’s development is lead by Yehuda Katz and Tom Dale. Yehuda Katz is also on the JQuery and Ruby on Rails core teams. As a rough indicator of community size, there are currently 351 questions on stack overflow. 6,676 stars on GitHub. Ember just recently released its version 1.0, and before that was undergoing a good deal of changes. But despite the turbulent state of ember, several companies decided to take the plunge into using ember as their client-side framework. Ember data is still not in its version 1.0 state, and it does not yet ship together with ember.

Ease of Learning

The documentation for Ember, though not as impressive as that of Knockout or Angular, is quite comprehensive and easy to go through. There is a 30 minute video demonstration to get started, and then a complete development guide full of examples. Ember certainly has more concepts to understand than knockout and backbone, but getting started is relatively easy.

Compatibility and Extensibility

Since Ember uses handlebars for templating, it should be possible to plug-in any 3rd party plugin for extending templates. Out of the package, ember offers custom helpers for use in templates and the REST adapter for controlling how your application handles requests to the server. If you need to integrate ember with 3rd party libraries, you may need to set embers “EXTEND_PROTOTYPES” option to false. In this case you will have to manually tell ember to extend your objects by wrapping them in what ember calls convenience methods.

Testing and Debugging

Like the other javascript frameworks, developer tools like chrome web inspector work great for debugging ember (see this great debugging demonstration by Tom Dale). Ember also works with jasmine for unit testing. One useful tip for testing the routing in an app is to specify the “LOG_TRANSITIONS” option when you instantiate your app as follows.

 

App = Ember.Application.create({

  LOG_TRANSITIONS: true

});
Unlike Angular, Ember does not currently come with any built in testing tools.

Pros

-Sophisticated templating for managing more complex views
-Can quickly and easily convert a multi-page site into single page app using the routing feature
-Fast development, easy to read markup, smaller maintainable of code
-Full-featured
-Easy to get started

Cons

-Relatively new framework (v1.0 just released)
-Ember Data is not yet at v1.0 so it is separate
-Lacks custom HTML tags like directives in Angular
-Lacks extensive testing tools like Angular
-Doesn’t integrate easily with 3rd party libraries

Conclusion – Which One to Choose

On one hand, each javascript framework has its unique advantages and it is reasonable to say that each one has its place in web development. On the other hand, these frameworks have a similar goal. They are all excellent frameworks and there will certainly be a place for each one as web apps continue to become more and more sophisticated. The factors that determine which one you choose will be based on a combination of your projects needs and your personal preference of how you as a developer like to accomplish things.

Knockout

For a small project or improving an existing project knockout is a great choice. It is simple to get started with and integrates nicely with other 3rd party libraries. And if you decide at some point that you need a more powerful framework like Angular or Ember, you will already be ahead of the learning curve since they all use data-binding in a similar way. If you want to keep knockout you can easily add things like routing and data-access features through a number of third party libraries.

For larger projects knockouts view model could start to get a bit complicated because computed data is not separated nicely from persistable data, and increasing numbers event handlers may become hard to keep track of.

Backbone

If you are an experienced javascript programmer looking for a mature, proven framework that provides MVC architecture to your code while maintaining as much low-level control as possible and offering easy REST api data access plus routing, backbone would be a good choice. For larger scale projects, or if you don’t like writing so much boiler-plate code, the plugin marionette.js could be helpful.

Angular

If you need a fully loaded framework that seeks to rival native apps, reduce the amount of time and code it takes to write a web app, and you can handle a somewhat steep learning curve Angular or Ember would be a good choice. It is very hard to choose between Angular and Ember as the both seek to do similar things.

Angular has appeal in the fact that it is backed by Google, it is somewhat more mature, and it offers some interesting features like custom HTML directives and integrated testing tools. It is also easier to localize it to run in specific areas of your app to avoid conflicting with other javascript libraries you are using elsewhere.

Ember

Ember like Angular is a fully loaded framework that seeks to rival native apps and reduce the amount of time and code it takes to write a web app with an even steeper learning curve than Angular. Some developers may prefer Embers style since it is modeled after the Cocoa framework in iOS development making it similar to programming native apps.


WARNING: gnome-keyring:: couldn’t connect to: /run/user/yyh/keyring-Gh7AV7/pkcs11: No such file or directory

$
0
0

The fix/workaround is as follows:

1. Do:

sudo apt-get install libpam-gnome-keyring

2. Logout
3. Login
4. When the pop-up for unlocking the default keyring comes up, do not simply type in the password to unlock. Instead, click “details” and select the “automatically unlock this keyring on login” radio button. Then enter your password to unlock the default keyring. You will now have linked the default keyring to the login keyring. Even if the pam module was installed, it is possible that the link was not established. In that case, the last portion of these instructions may still solve the problem.

THIS FIX MAKES THE gnome-keyring-pkcs11.so LOAD AT STARTUP FOR THE LXDE SESSION

This is one of them, now fixed….Warning message, it’s an error message cause it is not letting the command to run. ”WARNING: gnome-keyring:: couldn’t connect to: /tmp/keyring-<SOMEGENERATEDSTRING>/pkcs11: No such file or directory ”This is caused because of a module: gnome-keyring-pkcs11.so that is not being loaded when you log into an LXDE session. (this problem is analog to other sessions too, in this case I am using LXDE but could be KDE, GNOME etc… see the solution to understand how).

Here is the FIX!!!:
add “LXDE;” (without the double quotes and plus the semicolon, didn’t try without it)
to this file “/etc/xdg/autostart/gnome-keyring-pkcs11.desktop”  by editing it with whatever text editor you preffer, I’ll use “nano”
1 – in a terminal type (as root or with sudo): nano /etc/xdg/autostart/gnome-keyring-pkcs11.desktop
 

[Desktop Entry]

Type=Application
Name=Certificate and Key Storage
Comment=GNOME Keyring: PKCS#11 Component
Exec=/usr/bin/gnome-keyring-daemon –start –components=pkcs11
OnlyShowIn=GNOME;Unity;LXDE;
X-GNOME-Autostart-Phase=Initialization
X-GNOME-AutoRestart=false
X-GNOME-Autostart-Notify=true
X-GNOME-Bugzilla-Bugzilla=GNOME
X-GNOME-Bugzilla-Product=gnome-keyring
X-GNOME-Bugzilla-Component=general
X-GNOME-Bugzilla-Version=3.2.2
NoDisplay=true
X-Ubuntu-Gettext-Domain=gnome-keyring
2 – after editing, make sure you save the changes
3 – RESTART YOUR PC
CHEERS!

Git push requires username and password

$
0
0

A common mistake is cloning using the default (HTTPS) instead of SSH. You can correct this by going to your repository, clicking the ssh button left to the URL field and updating the URL of your origin remote like this:

 git remote set-url origin git@github.com:username/repo.git


Send method in Ruby

$
0
0
x = [1,2,3]
x.send :[]=,0,2
x[0] + x.[](1) + x.send(:[],2)


x = [1, 2, 3]
x[0] = 2
x[0] + x[1] + x[2]

Ruby

$
0
0
def palindrome?(string)
    letters = string.downcase.scan(/\w/)
    return letters == letters.reverse
end

palindrome?("A man, a plan, a canal -- Panama")  # => true
palindrome?("Madam, I'm Adam!")                  # => true
palindrome?("Abracadabra")                       # => false (nil is also ok)

def count_words(string)
    words = string.downcase.split(/[^a-zA-Z]/).reject{|e| e.empty?}
    wf = Hash.new(0)
    words.each { |word| wf[word] += 1 }
    return wf
end
count_words("A man, a plan, a canal -- Panama")
    # => {'a' => 3, 'man' => 1, 'canal' => 1, 'panama' => 1, 'plan' => 1}
count_words "Doo bee doo bee doo"
    # => {'doo' => 3, 'bee' => 2}

class WrongNumberOfPlayersError <  StandardError ; end
class NoSuchStrategyError <  StandardError ; end
 
def rps_game_winner(game)
    raise WrongNumberOfPlayersError unless game.length == 2
    s0 = game[0][1].upcase
    s1 = game[1][1].upcase
    raise NoSuchStrategyError unless ["R", "S", "P"].include? s0
    raise NoSuchStrategyError unless ["R", "S", "P"].include? s1
    if ( s0 == s1) 
        return game[0]
    end
    if (s0 == "R")
		return (s1 == "S" ? game[0] : game[1])
    elsif (s0 == "S") 
		return (s1 == "P" ? game[0] : game[1])
	else
		return (s1 == "R" ? game[0] : game[1])
    end
end

rps_game_winner([ ["Armando", "P"], ["Dave", "S"] ])

def rps_tournament_winner(tournament)
   s0 = tournament[0][1]
   if (s0.is_a? String) 
      return rps_game_winner(tournament)
   else
      return rps_game_winner([rps_tournament_winner(tournament[0]), rps_tournament_winner(tournament[1])])
   end
end
rps_tournament_winner([
    [
        [ ["Armando", "P"], ["Dave", "S"] ],
        [ ["Richard", "R"],  ["Michael", "S"] ],
    ],
    [
        [ ["Allen", "S"], ["Omer", "P"] ],
        [ ["David E.", "R"], ["Richard X.", "P"] ]
    ]
])

def combine_anagrams(words)
    hash = Hash.new()
    words.each do |word|
        sorted_word = word.downcase.split('').sort.join
        if hash.has_key?(sorted_word)
            hash.store(sorted_word, hash.fetch(sorted_word).push(word))
        else
            hash.store(sorted_word, Array.new(1,word))
        end
    end
    return hash.values
end

combine_anagrams(['cars', 'for', 'potatoes', 'racs', 'four', 'scar', 'creams', 'scream'])

class Dessert
    attr_accessor :name
    attr_accessor :calories
    def initialize(name, calories)
        # Your code here
    end

    def healthy?
        if (calories < 200) 
		    return true
		else 
		    return false
		end
    end

    def delicious?
        return true
    end
end


Git Real Slides

$
0
0

Level 1 Git Basic

# get help
$ git help config

# git config
$ git config --global user.name "Gregg Pollack"
$ git config --global user.email gregg@codeschool.com
$ git config --global color.ui true

# Start a repo
$ mkdir store
$ cd store
$ git init
Initialized empty Git repository in /Users/gregg/store/.git/

# status
$ git status
# git add files
$ git add --all
# commit
$ git commit -m "Create a README."
# log
$ git log

Level 2 Staging and remote

# show the unstaged differences since last commit
$ git diff
# show the staged differences
$ git diff --staged

# HEAD refers to last commit. remove the staged content to unstaged. 
$ git reset HEAD LICENSE
$ git status

# Blow away all changes since last commit
$ git checkout -- LICENSE
$ git status

# add and commit the modified content. But does not add new files, only add tracked content. 
$ git commit -a -m "Modify readme"

# Reset into staging and Move to commit before 'HEAD'. 
# undo last commit and put changes into staging
$ git reset --soft HEAD^
$ git status

$ git commit --amend -m "New Message" # Change the last commit

$ git reset --hard HEAD^ # Undo last commit and all changes

$ git reset --hard HEAD^^ # Undo last 2 commit and all changes

$ git remote add origin https://github.com/Gregg/git-real.git
$ git remote -v

$ git push -u origin master
$ git pull

$ heroku create
$ git remote -v

$ git push heroku master

Level 3 Cloning and Branching

#1 - Downloads the entire repository into a new git-real directory. master
#2 - Adds the ‘origin’ remote, pointing it to the clone URL.
#3 - Checks out initial branch (likely master).
$ git clone https://github.com/codeschool/git-real.git

$ git clone https://github.com/codeschool/git-real.git git-demo

# create a branch
$ git branch cat
# check branch
$ git branch
# switch to a new branch
$ git checkout cat

$ git checkout master
$ git merge cat

# Deleted branch cat
$ git branch -d cat

# creates and checks out branch
$ git checkout -b admin

Level 4 Collaboration Basics

$ git push # push the changes to remote server
# git push may generate error. git pull is used to pull down the code and push it up again. 

# 1. sync local repository with the remote repository. (git fetch) fetch the master in the remote server to origin/master branch in local server. 
# 2. merge the origin/master with master branch. 
$ git pull

$ git push

# git pull may fail with conflicting files. 
$ git status
$ git commit -a

Level 5 Remote Branches & Tags

# When you need someone to work on your branch. 
$ git checkout -b shopping_cart
$ git push origin shopping_cart

$ git add cart.rb
$ git commit -a -m "Add basic cart ability."
$ git push

$ git pull

$ git branch
* master
$ git branch -r  #check remote branches
origin/master
origin/shopping_cart
$ git checkout shopping_cart  #check the remote branch
$ git branch
master
* shopping_cart

# check remote branch
$ git remote show origin   

# Delete remote branches
$ git push origin :shopping_cart
 To https://github.com/codeschool/git-real.git
- [deleted] shopping_cart
$ git branch -d shopping_cart
error: The branch 'shopping_cart' is not fully merged.
If you are sure you want to delete it, run 'git branch -D shopping_cart'.
git branch -D shopping_cart
Deleted branch shopping_cart (was ea0a1b9).


$ git commit -m -a "Add ability to pay."
[shopping_cart 9851887] Add ability to pay.
1 file changed, 1 insertion(+), 1 deletion(-)
$ git push
Everything up-to-date
$ git remote show origin
Gregg
Remote branches:
master tracked
refs/remotes/origin/shopping_cart stale (use 'git remote prune' to remove)
$ git remote prune origin
Pruning origin
URL: https://github.com/codeschool/git-real.git
* [pruned] origin/shopping_cart

# Heroku deploys only master branch Would not work, would push to staging heroku-staging
$ git branch
* staging
master
$ git push heroku-staging staging:master

# A tag is a reference to a commit (used mostly for release versioning)
$ git tag   # check tags
$ git checkout v0.0.1  # check out code at commit
$ git tag -a v0.0.3 -m "version 0.0.3"  # To add a new tag
$ git push --tags  #To push new tags

Level 6 Rebase Belongs to us

# Merge commits are bad. 
$ git fetch
# 1. Move all changes to master which are not in origin/master to a temporary area.
# 2. Run all origin/master commits.
# 3. Run all commits in the temporary area, one at a time.
$ git rebase

$ git checkout admin
$ git rebase master
# if all goes well, then merge them together. 
$ git checkout master
$ git merge admin

Level 7 History and Configuration

$ git log
# Colorizing the log
$ git config --global color.ui true
$ git log

$ git log --pretty=oneline
$ git log
08f202691c67abd12eb886b587ac7b26d51005c7 Update index

$ git log --pretty=format:"%h %ad- %s [%an]"
#patch
$ git log --oneline -p
# Stats
$ git log --oneline --stat
# graph
$ git log --oneline --graph
# Date ranges
$ git log --until=1.minute.ago
$ git log --since=1.day.ago  #since (days)
$ git log --since=1.hour.ago #since (hours)
$ git log --since=1.month.ago --until=2.weeks.ago # since & until (relative)
$ git log --since=2000-01-01 --until=2012-12-21 # since & until (absolute)

$ git diff
$ git diff HEAD # difference between the last commit and current state. 
$ git diff HEAD^ #parent of latest commit
$ git diff HEAD^^ #grandparent of latest commit
$ git diff HEAD~5 #five commits ago
$ git diff HEAD^..HEAD #second most recent commit vs. most recent
$ git diff f5a6sdfsfsdfsdfff9..4sdsdfsdfsdfsdffb063f  # range of SHAs
$ git diff master bird  # branch difference
$ git diff --since=1.week.ago --until=1.minute.ago # time-based diff

$ git blame index.html --date short

$ git config --global alias.st status #git st
$ git st
$ git config --global alias.co checkout #git co
$ git config --global alias.br branch #git br
$ git config --global alias.ci commit #git ci


New Esri Open Source Javascript Projects: Esri-Leaflet, Geoservices.js, Terraformer, Pushlet

$
0
0

New Esri Open Source Javascript Projects: Esri-Leaflet, Geoservices.js, Terraformer, Pushlet

  •  154 342 537

We are happy to announce four open source Javascript projects we’ve been working on in the last few months!

  • Esri-Leaflet is a Javascript library to help developers build lightweight applications using the Leaflet Javascript mapping library.
  • Geoservices.js is a Javascript client for accessing ArcGIS Online services from the browser or Node.js.
  • Terraformer is a geometry toolkit for working with different geometry formats and building geo databases.
  • Pushlet is a simple API for sending notifications through the Apple and Google push notification services.

 

Esri-Leaflet

Esri-Leaflet is a Javascript library to help developers build lightweight applications using the Leaflet Javascript mapping library. It works well with Terraformer for converting data between different geo formats, and geoservices-js for making advanced requests to ArcGIS REST services such as place finding and reverse geocoding. If you’re familiar with Leaflet, you’ll feel right at home using this library. All of the Leaflet conventions are followed, so you can use ArcGIS services in a familiar style. One of the major benefits of using Esri-Leaflet over accessing the ArcGIS map tiles directly is this library handles displaying the correct attributions depending on the visible map area. Esri-Leaflet also has support for displaying retina basemaps!

Demographic Data Service

Below is an example of displaying a tiled ArcGIS map service over the gray basemap in an Esri Leaflet map. 
It’s as simple as this code snippet!

var baseUrl = "http://services.arcgisonline.com/ArcGIS/rest/services/Demographics/
USA_Tapestry/MapServer";

tiledLayer = L.esri.tiledMapLayer(url, {
                opacity: 0.25,
                zIndex:2,
                detectRetina: true
              }).addTo(map);

Bike Routes

Of course we’re not limited to displaying just points or polygons, lines work too! Here is a map showing bike routes in Portland. Bike Routes

L.esri.featureLayer("http://services.arcgis.com/uCXeTVveQzP4IIcx/arcgis/rest/services/
bike_rte/FeatureServer/0", {
  style: function (feature) {
    return getStyle(feature);
  },
  onEachFeature: function(geojson, layer){
    layer.bindPopup("<h3>"+geojson.properties.BIKEMODE+"</h3><p>
" +(geojson.properties.PREFIX ? geojson.properties.PREFIX : "")+
" " +(geojson.properties.STREETNAME ? geojson.properties.STREETNAME : "")+
" " +(geojson.properties.FTTYPE ? geojson.properties.FTTYPE : "")+" "); } }).addTo(map);

More Demos

Check out more demos on the Esri-Leaflet Github page!

Geoservices.js

Geoservices.js is a Javascript client that allows you to retrieve data and make queries against ArcGIS Online and to other services that adhere to the Geoservices specification. It works both in the browser:

<script src="browser/geoservices.js"></script>
<script> var client = new Geoservices(); </script>

and from Node.js!

var Geoservices = require('geoservices'); 
var client = new Geoservices();

Currently it can be used to consuming free services, and you can also use it to authenticate and consume paid services. For now, it supports geocoding and feature services, and will soon support the directions and routing services and eventually the geometry service. Here is sample code for using the simple geocoder service:

client.geocode({ text: "920 SW 3rd Ave, Portland, OR 97204" }, function (err, result) {
  if (err) {
    console.error("ERROR: " + err);
  } else {
    console.log("Found it at " + result.locations[0].feature.geometry.y + ", 
" result.locations[0].feature.geometry.x);
  }
});

Terraformer

Terraformer is a geometry toolkit for working with different geometry formats and building geo databases.Terraformer is a lightweight Javascript library that can be used both from the browser and from Node.js. You can use Terraformer to

For example, Terraformer can load in a file of all the US counties, build an R-Tree index, and can then quickly query which county a given latitude/longitude is within. US Counties in Terraformer

Pushlet

Pushlet is a simple API for sending notifications through the Apple and Google push notification services.If you’ve ever had the pleasure of working directly with the Apple APNS API, you know how difficult it can be to get it right. To connect to APNS, you have to configure SSL certificates to authenticate, and then it requires maintaining a persistent socket connection to the servers rather than making a new connection for each push notification. Pushlet is a stateless wrapper around the API designed to make it easier to use, like you’d use a typical HTTP API. Pushlet uses Redis as a temporary data store to store certificates, and maintains the socket connections to the APNS service so you don’t have to worry about it. Using Pushlet to send a notification is as simple as making a POST request to the Pushlet endpoint like the following:

{
  "appId": "com.example.iphone",
  "deviceId": "809f1d3237cd219c1c672bb141f6e18513fd86a073479ef295fd0e1687270853",
  "mode": "production",
  "notification": {
    alert: "The quick brown fox jumps over the lazy dog"
  }
}

If Pushlet doesn’t yet have the certificate for this appId, it will respond with this message:

{
  "response":"error",
  "error":"missing certificate"
}

Then you just re-send the message including the push certificate.

{
  "appId": "com.example.iphone",
  "deviceId": "809f1d3237cd219c1c672bb141f6e18513fd86a073479ef295fd0e1687270853",
  "mode": "production",
  "notification": {
    "alert": "The quick brown fox jumps over the lazy dog"
  },
  "cert":"-----BEGIN CERTIFICATE-----\nMIIFV...",
  "key":"-----BEGIN PRIVATE KEY-----\nMIIEvQI..."
}

after Pushlet successfully connects to APNS and delivers the message, it will wait for a specified time to see if the socket is closed (Apple’s way of indicating an error condition) and if the socket remains open, responds with “ok”.

{
  "response": "ok"
}

Of course once you’re certain your certificates are configured properly and have successfully sent push notifications to your devices, you can disable the timeout and speed up the HTTP response.

{
  "appId": "com.example.iphone",
  "deviceId": "809f1d3237cd219c1c672bb141f6e18513fd86a073479ef295fd0e1687270853",
  "mode": "production",
  "notification": {
    alert: "The quick brown fox jumps over the lazy dog"
  },
  "timeout": 0
}

{
  "response": "sent"
}

Since Pushlet does not store device tokens, and is not the primary store of push certificates, you can easily run multiple instances of Pushlet on a cluster of machines to increase throughput.

This entry was posted in App Developers. Bookmark the permalink.

网友印度出差见闻

$
0
0

上个月中,受到印度方的邀请,和三十几位同僚一起造访了孟买。30几位中我们十位中国人是出公差,还有十位是欧美和中东国家的同行。剩下的十几人是国内大学去短期进修的研究生,还有几个驴友。他们特别先行出发,一来是为我们打前站,二来也是趁公差前有空去印度旅游一圈,虽然最后我们的行程不一样,但是月底的时候都会在孟买碰头,于是大家就这么联系上了。

我们和几个欧美佬中有几个人去过印度,印象都不好,出发前纷纷来和我们分享他们当年在印度时的各种“离奇经历”,加之最近新闻报道印度频繁闹XX案,吓得队伍里几位女同胞强烈向领导请示要退出。不过我们一大帮人大都是走过南闯过北,近的说国内边疆地区,远的东南亚,南美,黑非洲都去过,合计着印度不就是脏点,破点,乱点吗,再过份有非洲脏,有南美乱吗?所以觉得没必要战战兢兢的,搞得去次印度就和下油锅一样。

这次印度行,严格说要从买机票算起。去印度的航班,我们第一反映当然是问问它的国家航空公司,印航。但是
印度,这个翻个喜马拉雅山就到中国的南亚国家,这个和中国一样是金砖国家的成员国,它没有从德里或者孟买飞北京的航班!只有飞上海的!而且这就是唯一一条他们飞中国内地的航班!上海-德里-孟买!买机票时,中介强烈不推荐印度航空,强烈推荐飞国泰航空走香港直达孟买。但国泰比印航贵,我们心疼荷包,还是买了印航,从上海出发。

关于印航,出发前我发现两个很好玩的事情:
1,在那个国际上给各个航空公司打分的网站上,如果看似是印度ID,给印航的都是5分,如果是其他ID都给印航打一到两分;
2 ,所有飞往印度的国际航班,不论是去德里还是去孟买,还是成奈或者班加罗尔,到达时间都是凌晨,出发时间也多是凌晨或者大清早。不管国际国内,机场一般凌晨是关闭的,但这个时段貌似是印度各个机场很忙碌的时间,不知作何解!

这印航的体验说实话不太好,去印度那趟我可以给三分,回来那趟我真想给一分!最头痛的问题是一进机舱,“气味”比较大,很像脚臭和酸奶混合在一起的味儿。从上海起飞前,空嫂特地拿着清新剂把机舱喷了一遍,回来就没这待遇了。飞机的配置挺好,还有舱外摄像头可以看飞机第一人称视角起降,比达美的加航的777要好,问题是保养的挺差,觉得像是用了十几年了,好多座椅上的蒙皮都破损了,个人娱乐系统不停的出问题,比如我前面的显示屏看着电影突然就死机黑屏了,再看周围好多都黑屏啦。

不过印度乘客都挺能聊,挺和气。以前传言说印度人不喜欢外人碰他们孩子,这有点失真。我们在飞机上逗着几家印度人的孩子玩,又是抱又是捏的,大家都挺高兴,没啥不愉快的。印度人孩子就是黑点,但眼睛和耳朵大大的,挺像中东人的孩子,看着很可爱。

第二点要指出的,就是印度电影现在也不一定非要有跳舞唱歌的桥段。飞机上看的几部新的印度电影都很现代没跳舞的,但老电影就不停的有扭秧歌一样跳来跳去的。印度电影拍的镜头感不错,看前几分钟觉着和好莱坞大片一样很有气场!但,很快,就冒出印度电影的通病:各种夸张的镜头和慢动作!大家知道老港片里面发哥进场都是慢动作对吧,也知道恒源祥广告和西游记后转那神片里不停的重放三次那种烦人的手法吧,印度电影经常通篇都是这样的镜头!慢动作加三次重放,还不停的学日本A片那样晃镜头来表现人物心理挣扎,看多了眼睛都要花了!

到了孟买,机场和印尼雅加达的建设水平差不多,当然和国内各种是不能比了,但是在发展中国家来看,也算可以啦。出机场后,遇到等待多时的印度方接待人员。印方人员非常热情,英语虽然印度腔很重,但交流一下觉得教育水平肯定不低,讲话很有条理,思维很快。来接我们的司机们也都着装统一得体,没哪个是蓬头垢面或者大胡子的造型,当然,脚上蹬的还是很“印度特色”的凉鞋—我后来发现,除了我们三十几号人,接待我们的印方人员,我们去过公司里面见到的高管,还有高级酒店和夜店里的,普通印度人确实都是穿凉鞋和拖鞋的。这貌似是他们的一种文化传统和标志,因为印航的空嫂们登机服务时也是把皮鞋换成凉鞋。

和印度孟买的交通相比,中国任何一个地方的交通都可以说是“秩序井然”了!我证明,印度有反光镜的车都是新的日本和韩国车,有点年头的车要么反光镜被拆了,要么好像从来就没装过!反光镜在孟买的道路上完全是毫无用处的!印度人开车想变道就变,后面有人按喇叭就变回来,没人按就变过去!停车和变道时的车距及其小,都是车门贴车门,保险杠贴保险杠。就这样的路况,印度人能把两车道开出6车道,同时大家以60公里以上的速度在限速40的路上飞奔!
我们的司机第一天就是这样开车的!在一大群大卡车中以110的速度狂飙,我们车上几位一路上给他加油,高呼牛B,每次以夹缝的距离高速超过一辆卡车我们就对司机报以热烈的掌声!但心理我们其实紧张的要死,到目的地时我们双腿都在打颤!这位司机老兄好像后来受到了警告,从第二天开始开车慢了,守规矩了,但也让我们少了一大乐趣。
孟买交通最糟糕的环节是在城郊接合部,大卡车拉着警报一样的汽笛呼啸而过。真到了市郊的公路上,车减少了很多,虽然路面大部分时候只有双向两车道,但还是很通畅的。郊区公路上的杀手不是车,而是各种横穿公路的动物,常见的有牛,有猴子,有狗,还见到过马,见过羊从路中穿过,偶尔还有大群的乌鸦或者鸽子突然群降在路中间。印度这点很有趣,不管是城里还是郊区,走着走着很容易碰见野生动物。这些动物对人类是警惕而不敬畏,你手上有吃的它们就凑过来,不然就各走各路互不干扰。特别是狗,街上到处睡着狗!我在印度半个月,就没见过一条在跑在走的狗,全是各种姿势懒洋洋睡在地上的,除非被你逗起来了,平时真是睡得一动不动!

说说我们住的地方

我们住的是一家四星级的连锁酒店,母公司是阿拉伯的。和很多发展中国家一样,这酒店内外两重天,里面是古色古香的印度家具,恭谦整洁的门童随时等待着为你提行李按电梯,外面就是臭气熏天。我在印尼就体验过,酒店里面就是热带芳华,一拉开窗帘就是黑黢黢的低矮平房蔓延到天际。非洲国家更是如此,里面是土耳其地毯,外面是泥巴路。我们这家酒店坐落在孟买靠南边的中心城区,外面已经不算差,虽然味道还是重,至少污水和乞丐是很少见的。

在这家酒店我们住了大半个月,大堂和门童们都熟悉了,也体验到了很多东西。

一是上网不灵:酒店房间配置很好,和国内四星级一样,硬件上要的都有,软件上随时一个电话服务生立刻就上来。有几位幸运的升级到了大套房,睡的床是雕花的大幔床,晚上服务生慢慢的帮你把帐子放下来,颇有一种体验当年王宫贵族的爽感:当然,这几间大套房全被“转让给”几位擅长撒娇的女同胞了。但这酒店唯一硬件上的问题是上网不行,每个房间只有一个wifi的IP,一周分配了60个小时,网速很慢。这是很要命的,这年头没网络很多事情办不了。网速慢到打开邮箱要经常刷新,就不谈和国内视频了,QQ传文件也半天不动。而且不知道是有意还是无意,我们觉得上网计算时间的系统有问题,有一天我明明只上网一个小时,却显示我用掉了近四小时!所以一不小心,所谓60个小时很快就用完了!大家一开始没经验,用完了之后只能跑到附近的印度人那里蹭网,或者开国际漫游的数据流……

二是,从这个酒店我开始体验到,印度的服务生,佣人,门童之类的数量很多。很多中国一个员工可以搞定的,比如端茶送水擦桌子,印度会有三到五个!他们会一个只负责端茶,一个只负责倒水,一个只擦桌子,还要算上边上一个发号施令的小领班。后来在印度的公司我们也有体会,那就是在印度很多工作是不能僭越的:你是煎鸡蛋的,你就不是负责递东西的,哪怕你再闲而递东西的人再忙,你也不会去随手把手里用脏的锅递给洗碗工。经常我要发个传真或者明信片,总是大堂里一个领班代收,再传给一个助理,助理再传给去发传真的门童。哪怕那个门童站在我边上,他也不会收我的信件,而是把领班叫过来!所以印度人做事常常一个简单的步骤也有很多人,而且一定其中有一个是像大堂领班那样发令起头的,否则大家就不知如何是好了。开始我们的揣测是这是为了保证就业,或者门童确实文化水平太差。后来我们渐渐觉得,这就是一个很隐晦的概念:阶级……

三是印度的服务生不是在服务你,是在伺候你……真的。这些服务中,你会觉得有一种在中国不会体会到的距离感。比如,在中国我们有打扫厕所的,但没有趴在地方跪在地方擦厕所吧,印度就是这样。比如我卫生间要换块澡巾,电话一打来了三个人,一个领班带了两个帮手,领班一边向我抱歉说刚才打扫的人疏忽了他们会加强管理,一方面就叫两个人,一个负责换澡巾,另一个呢?擦卫生间地板。我和邻班说话时,他就爬在地上,在我们两个的胯下一点一点的擦地板,擦干净了也没起来的意思。后来我发现,只要我和领班在讲话,他就要爬着擦地板,哪怕地上很干净没啥好擦的!不光是卫生间,公司的电梯里,学校的食堂和走道,都会不时有个小黑人在你的胯下擦地。特别是在电梯里,几个人在个封闭空间里说话,我们腿下面是个人趴着擦电梯门,感觉特别怪,不自在。我们有时都求清洁工,能不能站起来弯腰擦呀,因为不习惯看几个人跪着,他们还不停的扫里,扫里(sorry),然后继续趴着擦……

四是印度的水,至少酒店里的是能喝的。关于印度卫生状况最夸大的就是不能喝水,不能吃当地的东西,甚至刷牙水都要是瓶装的。我们有位仁兄就被吓大了,满满一个箱子,托运了一箱子矿泉水!其他啥都没带!我在酒店里刷牙就是用的自来水,洗澡时也有水入口了,也没出问题。我们几个去印度前就说好了,到了当地,一不吃洋快餐,二不吃泡面,三不在酒店里吃,就要到外面去找个地道的印度餐馆,大不了就腹泻呗,拉着拉着免疫力不就出来了呗!关于在印度吃饭,我会专门单独讲,这里不重点说。总之,星级酒店的自来水还有可以的,千万别到了见水变色的地步。不过街头小贩的水是有风险,我们几次尝试底线后,基本可以靠闻味道和闻杯子来分辨什么水能喝什么不能喝。这里要说一点,你要是喝了印度的自来水腹泻了,有可能还不是水中细菌没消毒的问题,而恰恰是这水消毒过度处理不当,细菌不是罪魁祸首而是各种水中残留的化学物。

五,这印度的酒店宰客太厉害。我们的酒店每个单人间每天收300欧元!还是公司内部协议价!我们一致认为这酒店的水平在国内就是一晚800人民币,在美欧就是200美元封顶!而在孟买这样一个白领月薪200到300美元的地方,一晚上敢要收300欧元!如果不是印方支付,恐怕没有哪位国人愿意付这笔钱,就冲着这点我们都表示印度接待方还是很出血了。我们后来了解下,印度的酒店,这档次都这价钱,童叟无欺,一致宰客。所以我们觉得如果想去印度旅游,要住的好点就无法省钱,比去新马泰贵多了,所以至少在开销上来看印度旅游的优势不大呀。
哦,对了,这钱还没算税,没算服务费,没算服务费上再加的一层税!

说道这里要普及下印度卢比的汇率。
一美元,能换53到55卢比,也就算是简单点,50卢比吧。所以一个印度中产,一个月收入300美元,就是一万五千卢比。

记住一点,你到一个第三世界国家当地去消费,除非是津巴布韦或者越南拿着膨胀到发指的地方,就不要去想当地货币和美元和人民币的汇率,否则你会抓狂。打个比方,一卢比对当地人来说,购买力就和50美分差不多,他们吃一顿饭一般就30卢比,我们在公司的食堂订到60卢比的午饭已经是非常非常好。很多当地员工中午的外卖,就10卢比上下!所以想想看,一万5千卢比在当地真不算差的工资,是可以叫中产了!孟买的低收入阶层,每个月的收入只有几百卢比,换成美元就10刀不到!

我们开始几天在酒店里订餐,一顿饭两个人要800卢比!我们开始觉得太宰客了,但是换成人民币算算,每个人50块钱,还让人家伺候来伺候去的,还吃的安全点,值了。可是,这个价钱当地人看来是很高的。我们印方的接待人员就经常吃过饭才来找我们,因为在酒店里他们实在自费请不起,公费控制又挺严…私企吗,当然严格…

一位印度人和我们说他在孟买大学商学院,毕业后他可以拿到相当于人民币一万的月薪,我们知道这在孟买确实已经是很不低的水平。而印度大企业的那些高管是和欧美的薪资水平平级的,换算成卢比在当地就是相当惊人的数字了。那些做IT外包的人,拿得是欧美标准的酬劳,瞬间就会和当地人拉开差距。所以,在印度,哪里有IT外包中心,那里的地价马上就会飞涨!

没找到红灯区,也不方便问。但是印度同仁隐晦的透露,还是有的。我们这次出去事关企业形象,双方又是第一次接触,直接开荤还是不太好的。
娱乐节目就是看电影,看电视,坐在海边发呆。酒吧是有,但是很少,消费水平一晚上5000卢比样子,进去倒是有很多妹纸。但是印度妹纸都是家里男人带出来的,不敢随意去搭讪呀!印度人在男女问题上依然是相当相当保守的。
印度现在有30%到40%的人是文盲。剩下的人中,大概有一半会讲英语。下层人英语口音很重,中产阶级说的英语能听懂,高层的英语很地道。印度说英语的总人口超过了英国。
印度是全民信教,六分之一的人口是穆斯林,7成是印度教徒,剩下的10%就是各种其他宗教。包括佛教,基督教,犹太,拜火教,锡克教,等等。大街上寺庙和圣坛随处可见。
旅游就是吃住行加个安全。吃住是可控的,但出行在印度有点不靠谱,不放心就尽量坐飞机吧。安全的话,4到5个人结伴出行,肯定是安全的。别故意找些黑暗的角落,在那种地方出事了就是自己的问题了。

英国人给印度留下的最大遗产,就是窜连起了整个印度的铁路网系统。
铁路对印度的运输来说是至关重要的,至今这个国家内部的运输还极为仰赖这个英国人的遗产。印度有很多河流,但是没有水运。公路对印度来说是阿克琉斯之踵,国家发展的最大制约。
孟买城南的火车总站,是英国殖民建筑的遗产,叫做维多利亚车站。
在上孟买车站图之前,我先描述下孟买的地理构造。孟买是由几个半岛组成的港口城市,最大的两个半岛就是孟买市,和Mumbai Navi–现在新孟买城区的所在。这两个半岛像爪子一样伸入阿拉伯海,在南部的爪尖中隔着大海,也就是孟买湾。在北部的爪子根部,几个半岛联在一起,由公路桥相连。
孟买南部靠阿拉伯海的地方,是当初英国人上岸的地方,也是殖民统治的政治文化中心。现在孟买城市标志之一的印度之门,就是英王当年每次造访印度时靠岸登陆的地方。

这就是印度之门,孟买的标志。一边是阿拉伯海,一边是孟买城

1

孟买的铁路系统从南部的爪尖部位一直延伸到北部的爪根,贯穿孟买城和周边几个半岛。
这个铁路,分为国家铁路和城铁两部份。国家铁路就是我们观念上的火车,从一个城市开到另一个城市。城铁的作用大致相当于我们的地铁,在市内停的快速交通,每天早高峰把住在北部郊区的上班族运到南部,晚上再把下班的人从南部运回北部的郊区。考虑到孟买城市道路极端的拥堵,开车走一分钟就要堵10分钟,因此坐小火车是在孟买市内最靠谱的交通方式了。

印度的国家火车票分为7个等级,我们曾有位同仁周末试过第七等级的车票,从孟买到艾荷拉巴德,基本就是扒车顶。我不知道孟买的城铁有几个级别的车票,我只知道最贵的一等票,从我们在的地方到城南区要60卢比,最便宜的那个等级只要4卢比!

孟买的城铁车厢是开放式的,没有门,很多人半边身子挂在门外,一是为了风凉透气,二是为了好玩(常有年轻人作各种开挂特技),三是为了上下车方便,这里人都是不等停稳就跳车上下的,火车和马路上的公车还有卡车都是如此。

2

印度的铁轨,一种神奇的腐朽,枕木都烂了

3

火车开动时,很多人也是把身子探在外面

4

这火车开得不慢哦,大概时速60到70公里的样子,我们试了一下,这时把半边身子挂在车外,吹着风顶着太阳,确有一种独特的快乐感。

5

这是大家在媒体上最常见的印度车了,英文翻译过来叫“大使”牌。这车是仿造60年代一款菲亚特的设计,一直生产到21世纪,知道最近几年才停产。虽然这几年来孟买出租车队伍里面也多了不少现代和本田的身影,但老大使依然是绝对的主力。

6

孟买的房价无上限,全球最昂贵的那个27层楼私宅就在孟买!总体来说孟买的地皮快和纽约差不多了,能在孟买市中心弄个办公室是件很NB的事情!

一方面是孟买没有规划,第二点来说是这个国家没有任何调控措施。所以这中高房价和低收入联合起来造成两个现象:一是窝居,这点我们中国人很好理解

第二就是上班族都住在郊区,连服务这些上班族的饭店和商店的也都在郊区,盖因市区实在是地价太高。这些上班族每天中午要吃饭吧,但周围因为没有地皮太贵没有饭店,所以只能打电话给十几公里外自己郊区家附近的餐馆订外卖!
然后每天中午,颇为壮观的,就有一大群小黑人,每个人扛着几十份外卖,从十几里外的郊区坐火车进城送餐!因为餐具不是一次性的,而是锡筒,小黑人们等上班族们用餐完毕,再收拾好餐具,驼回十几公里外的郊区餐馆。

我等下会说印度的文化差异,就会提到人种问题。印度东南西北各地的人,感觉就不是一个人种。我们刚来孟买时也差异,怎么大街上,包括校园里,都是小黑人小黑妹,没有印度电影里那些俊男美女啊。后来我们去了泰姬酒店参加一次晚宴,天哪,满场都是明星脸的帅哥美女,皮肤白鼻梁挺,眼睛是棕褐色的,更像巴西那里的南美人。女人都是高挑的S曲线,男人都是一米九,和酒店外大街上的各路小黑人们相比,真心不觉得是一个人种呀!在印度要看帅的靓的,就要去高级的社交场合了。一般大街上偶尔碰见花花草草,大概也就是一千人里面才一个的比率吧。

印度还有一种高层的人种,长相不像普遍的白人,也不像阿拉伯人和其他印度人,也不似我们东亚人,而是特别像佛祖!我们碰到的很多政府官员都是方面大耳,侧面看就是一个佛像脸。

孟买随处可见睡在地上的狗。这些狗都很瘦,我们想可能是因为印度全民吃素,所以狗也找不到肉吃。没肉吃就没精神,为了节约体力狗儿们平时就趴在地上睡觉

7

这应该算是孟买的景观路了吧,甘地大街,两边保留了很多英国殖民时代的建筑。

8

南城区是孟买最漂亮的地方了,这里有阳光海滩,连绵的林荫大道,保留了很多英国人时代恢弘的建筑。这些建筑包括了今日的孟买大学,孟买的火车站,还有税务局和法院。不过这里的餐馆和摊贩宰客也很厉害。

9

孟买最豪华的酒店,泰姬马哈尔酒店的辅楼,遥望阿拉伯海。这个酒店属于TATA集团。在印度,貌似所有和泰姬陵有关的商标,都属于TATA集团。

10

这是泰姬酒店的主楼。因为以前在这里发生过针对外国人的恐怖自杀炸弹袭击,所以酒店的安保比较重视。进门要拆开包给保安看。

11

孟买路口等红灯时的车距。隔壁的车触手可及

12

孟买的一栋住宅楼,下面是商铺。孟买高架上两边都是这样的住宅楼,黑黑旧旧的。我这真不是想黑印度,而是我在孟买见到的平民住宅楼多为此,有些郊区的政府办公楼也是这样的。虽然很破,但是貌似没有修缮的想法,也不刷油漆粉饰一下。据印度同仁说,别看这些楼外观很烂,但是结构用料很好,所以就算墙全烂了钢筋外露了楼也不会塌……

我想,这恐怕就是印度特色的腐朽,腐而不烂,烂而不塌,因为不塌,所以住在其中和周围的人也没有想过要做维护和修缮。

13

这是那座著名的富豪豪宅,有二十多层供这家人住,顶楼有直升机平台。个人感觉,设计真是不好看,既没气势也没美感。不过据说业主平时都在阿联酋,这栋楼现在主要是他400多号佣人在住

14

孟买的商业区,是没有规划的。一个专门店旁边就是一个黑而烂的平房,然后隔着过去一个又是专门店,然后又是两座黑屋……此照片拍摄于孟买最好的购物中心Phoenix High Street对面。印度人也很有意思,不许我们拍摄购物中心的照片,害得我们想拍点孟买好的一面的照片都拍不到。

15

这是商场内部,我就拍了一张,一个保安就过来表示不许拍照。

16

先普及一下个人对印度这个国家的看法吧。

我觉得一定要先描述一下这个国家的基本状况,下面谈到一些具体的见闻时,大家才有体会。

首先要说的,我不觉得印度是一个统一的国家!这点不是在埋汰印度,而是和他当年独立统一的方式有很大的关系,留下了不少隐患。
你如果尝试把印度比做一个共和国,或者一个民主制国家,或者一个联邦制国家,都会遇到很多难以解释和理解的地方。但是我们中国历史上有段时期,特别适合描述现在的印度。那就是先秦时期
不错,就是东周列国时期!印度现在这国家的状态就差不多出于我们东周列国时期。

第一条:尊王分封,春秋战国时期是这样的吧。中央是周朝,分封出去的各个王侯再建立了自己的诸侯国。西周时,中央强大,诸侯国就尊王,而到了东周时,皇室衰微,大家就不鸟中央纷纷自立门户了

印度今天的每一个邦,相当于咱们的省,都可以看作是一个咱们春秋时期的诸侯国,印度中央政府就相当于那时的周朝王室。每个邦,在立法和司法上有非常大的特权,特别是在税收上。从印度的一个邦,横跨到另一个邦去送货,那是要交税的!如果是从一个大的城市,比如孟买,到邻近的古吉拉特邦去运货,你不仅要交对方邦的税,还要给对方邦所经过的城市交税!基本和出国无异!在印度,交给中央的税,是叫DUTY,而tax则专门是指各个邦和城市的税收。
各位想象一下,这相当于我从北京送货到上海,一路上要给河北,天津,山东,安徽,江苏,和上海交税!
这导致在印度做物流非常的困难,还没出货呢,沿路的各邦的特派员就来问你要税了。在印度,货运公司有很多不是指我们意义上拉货的公司,而是专门帮你去摆平沿途各邦的各种税收衙门,好让你清关过卡的时候快一点。这些公司很有责任感的呀,我看到一家这样的公司说:如果你运的是生蔬海鲜,因为交税时耽误了,可以在当地免费提供一晚的冷库,第二天保证帮你摆平!

第二:书不同文,语不同音,度量不统一!
印度的官方语言是印度语,但是全国有近一半的人不会说!
因为印度有二十到三十个邦,每个邦都有自己的官方用语。这可不是我们粤语吴语那样的方言哦,而是彻底是从书写到文法上都不一样的语言!今天印度的钞票上,为了表示面额,就用二十三种主要语言一一印出。这还不算每个邦内部还有N种不同语言,如果一起加起来,全印度一共有300多种语言。
语言不通会造成什么样的困扰呢?
首先就是消除文盲很苦难。你在你们邦终于会用自己邦的语言写字了,到了隔壁的邦还是睁眼瞎,文盲一个!这给印度人在自己国家里面迁徙造成了很多障碍。有些人甚至一辈子走不出自己的村子,因为他说的语言和周围地方的不一样!
说道消除文盲,我要吐个槽。在印度,会写自己的名字和基本的语句,就不算文盲!但有的人,学会写名字之后,因为一直没机会写字,过了几天他又忘记了,所以印度的消除文盲比率上还要算上一个退化率!这是说这个人又从非文盲变回成文盲了

再有一个恶性的,就是文化垄断!大家可能会问,国家为什么不能推广印度语,就像我国普及普通话一样呢?
不能,很多邦为了保持自己文化上的“独立性”,就是不教印度语,别忘记了,每个邦在立法上的独立性哦。成奈和海德拉巴德就只用泰米尔语,不说印度语。这点很像加拿大的魁北克,为了保持自己法语的地位所以不教英语。我们碰到一个成奈出生的IT主管,在孟买上班,他就只能和同事说英语,因为他从小就没学过印度语。

如此一来,能把所有印度人联系在一起的语言竟然是英语,而不是属于本民族的印度语,这也是印度很悲催的地方。

为什么会是这样的呢?
印度当年从英国人独立的时候,并不是一个完整的国家!注意,整个印度当时是一个文化概念,而不是行政概念。英国人在的时候,灭了土著的王国,拉拢了地方的土司,硬是把几百个没啥联系的小国和土邦揉在了一起。
英国人在的时候有实力把大家揉起来,但英国人走了,怎么办?

于是以德里为中心的印度新政府就搞出了一个基本框架,附带很多协议,跑去和各个土司和王侯谈判,只要他们愿意签字加入印度这个大家庭,很多特权可以被认可保留。这类似我们搞的一国两制,印度搞的是一国多制,只要承认中央主权,你在自己的辖地上该怎么整就怎么整吧。于是大量的封建土司和宗族嫡系被保留了,直到今天还在印度的各地有巨大的影响力。
有些土司当年是很识大局的,签就签了。有些,特别是国外背景强点的,就不签。于是德里就号召当地人,学习甘地前辈的非暴力不合作,集体大BG,弄得土司们活不下去了必须要签。如果当地人很怕土司不BG呢?德里就会从邻近的邦发动觉悟高的群众,搞大行游,一路向这个不签字的土司老家行游过去,类似当年墨索里尼进罗马。
总之一句话,非暴力。
印度人不喜欢上片刀这样抄家伙的,这和宗教文化有关系。
这事情要分两面看,好的一面是在当年国家才建立的时候避免了流血,起到了迅速稳定的统一印度的目的

坏的一面是历史残余太多,导致印度今天就是个东周列国之类的,各个邦之间互相不买账,德里就是个吵架的地方。

这种不买账还发展成一种社会文化,扩展到印度的各个层面。一个公司里,各个部门互不买账。城市政府部门之间,互不买账;城市和城市之间,互不买账,而总体上城市和农村,也是互不买账。
这又导致印度的发展很多时候沦为一个邦,一个城,一个企业,甚至几个个人的单打独斗。提议永远都是有的,但就是少有进展,因为大家都是互不买账的

唯一能让大家买账的,就是宗教,还有种姓。前一个很光明,后一个则很苦涩。

谈了这么多印度的林林总总,还是先回到主题看孟买吧

出了遍布老殖民建筑的城南,孟买的街道基本就是这个造型的

这和我小时候家门口的菜市场差不多。要说干净有序,那绝对是没指望的,但要说到特别的脏乱差,那已经比贫民窟要强的太多。其实我个人对这种环境并不反感,相反觉得挺热闹的。一个街巷有了人气,有了吆喝的叫卖声,有时恰恰会让人产生一种亲切感。
这种街巷过过小日子倒也和谐,但非要背负上赶超上海那样的使命,就变成了一种自我折磨。
印度就如这个小巷街市,如果过过小日子,没人会觉得它不好,相反会觉得它很可爱,但这个国家自创始之初就背负了很多难以承受的远大理想。

在黑矮的平房中,新的大楼也在慢慢的爬起。孟买周围的工业不多,但城市空气也不好,恐怕也是因为整个城市四处开工的建筑扬尘导致的吧。

我看到这张印度男孩,就不想去回忆当时他身后的垃圾堆。把美丽的事物撕碎就成就了悲剧。如果孟买是和索马里一样,遍地是持枪暴徒,我们倒不觉得痛苦了,因为废墟和战乱在美学上是统一的,两个事物在概念上没有反差。但印度人民不是,把这些孩子纯真的笑脸和贫民窟,垃圾堆,漫天的乌鸦和满地的粪便联系在一起,我们头脑的逻辑就接受不了,恰因为我们本能上不能把纯真的美和悲惨的脏乱差拼在一块,这就让我们的头脑觉得压抑,心理觉得恐慌。

而孟买就是这样,偶有的高楼和大片的棚户比邻而立,互相之间不觉得有任何矛盾对立。

我一直想啊,我们中国现在也有很多脏乱差,但为什么我在中国的时候没感到不适应,而在孟买的街头常有想反胃的感觉呢。
回来后我想通了
中国确实很多地方很脏,但中国是该脏的地方脏,比如垃圾站,化粪池,三不管地区,修了N年的工地,饭店后巷等等;
而不该脏的地方,比如市民广场,车站,办公楼区,景观路,虽然不是尽善尽美,但是我们会尽力去打扫。
所以中国的脏是可预见的,可回避的。你在心理上是有准备的

印度不是这样。印度是该脏的地方,非常脏,脏的不忍直视,但你心理上还能接受,谁让你专门找这种地方去看的呢
但是印度不该脏的地方,也会脏,从医院,到中产阶级社区,还有寺庙周围。甚至有时在花园洋房遍布的富人区,走着走着你前方地面突然出现了一大坨牛粪(因为我不觉得狗能拉出那么一大堆),或者垃圾堆!而且看起来没有人想要介意去打扫一下的意思。这种脏是突兀的,你心理没准备的。你本来想着终于从贫民窟里开出来了,到海滩上来享受下热带的阳光,然后抬头一看一对对情侣坐在沙滩上各种垃圾堆间打情骂俏,这怎么受得了呀!

这是大使牌出租车的仪表盘和计价器,看着很怀旧吧。19卢比起跳,车费跳的可快了。但一般我们最多也就跳出过200卢比,那相当于是城中到机场的距离。

18

在孟买,常有出租司机看我们是外国人,就把计价器一关,然后开始耍宝了:你们外国人总是怕宰,嫌我们绕路,那这样,我们出发前商量个价钱,不管多远我们都按这个钱算,怎么样?我觉得从这里到你们要去的地方要400卢比。
遇到这种事,我们都坚决要求对方用计价器,这种预付车费的折腾法子就是为了宰客。一般开价400卢比的,实际用计价器只会跳到70,你就是和他杀到二五折,他还是赚。我们一般出发前都算过距离,真要是单程有400卢比,我们早就找酒店联系包车了,一天才一千多。

在德里,宰客更严重。德里比孟买还糟糕,孟买好歹有出租车,德里只有三轮小突突!小突突基本不用计价器,理论上15卢比起跳,一公里10卢比,但看你是外国人,肯定想办法要从你身上榨出个大几十。

小突突没啥好尝试的,既不舒服也不实惠,而且我们小时候都做过三轮车,没啥新鲜感。

孟买湾的油轮。

19

孟买港口很有意思,各种船只扎堆停,渔船,游艇,摆渡,远点就是工程船和油轮,还有军舰!大家加都停在一起,摆渡船就在大油轮和军舰间穿梭。
在孟买,从海边开出去不到五六分钟,就能贴到油轮的边上。

在孟买的时候,我们不敢让酒店洗衣服,怕的不是水质问题,而是据说整个城的衣服,从下里巴人到星级酒店的,都是送到孟买城著名的千人洗衣场去洗的。

这洗衣场是下面这样的景象,这也是网上很多孟买贫民窟照片的拍摄对象。

20

这是可能用来洗我们衣服的水池子

21

再多的图片我就不放了。这个洗衣场在一个城铁站边上,里面只有男洗衣工,因为女的挺不住这么重的体力活。这些洗衣工每个月收入只有几百卢比,10美元不到。
所以我们都是在酒店里用冲屁的水管洗衣服……

如果大家觉得这就是贫民窟的景象的话,那就口味太轻了。真的贫民窟,我们曾开车路过,一停车,车窗外围满了小黑娃乞丐。你要拿相机去拍他们,他们就用力砸窗户,大叫one photo 10 Rubee! 所以我们没在真正的贫民窟拍照。那地方想下车,连立足的地方都没有。

印度在传媒,影视,娱乐,软件和创意等和第二产业挂钩不大的方向上发展的都很好,我们参观了他们的媒体集团和影视基地(可惜又是不给拍照,不然真是美女帅哥满照片的飞),觉得他们的思路很新,商业模式很纯熟,人才培养也完备,在具体的制作和创意方面比我们的文化产业强,很多中高层都是直接从好莱坞和欧洲高薪聘请的,这方面印度的投入很巨大。

但是一说道和第一产业与第二产业相关的行业,印度就呜咽了。制造业和基建是印度的痛中之痛,工业和农业上都没什么亮点,哦,对了红茶和香料等特种农作物除外。在独立前,英国人给印度人留下了全印铁路网,但是刻意没有在印度展开任何工业基地的建设!印度独立后,基建在相当长一段时间依赖苏俄的援助,90年代后依赖日本,21世纪初是韩国,而现在的电信方面是中国。制约工农也发展的两个重要问题是物流的困难以及人口制度缺位。

此外,涉及到政策整体规划的行业,印度也做的不好,比如金融。孟买的证券市场历史悠久,而且和中东及东非地区的资金流颇有联系,但搞了半个世纪,就是没做出任何地区影响力!

但印度不是一点工业都没有,像最近在国际上很火的TATA集团,还有Reliance等等。印度工业的发展力在最近十年才开始释放,慢慢登上国际性的舞台,在某些很尖端的方面有一席之地,但总体上依旧属于只能在印度自己国内小打小敲。不过有些印度企业在代工我们国家品牌的产品了!在印度生产,返到中国贴个中国企业的标签,再卖到欧美去,呵呵,全球化真是奇妙。

在印度做生意,我们观察了一下,觉得非常非常难!一是这个国家政策缺失,各种条令复杂而自相矛盾,作为非印裔的外国人要想往印度注资并在当地展开商业活动,那真是异常的难;二是微观上,印度群众的购买欲望很大,购买力却很小,作为一个外国公司,如何设计一个满足印度客户群体需要的产品,是很头痛的问题。总体形容,这个国家的顾客就和这个国家的领导人一样,眼高手低,脑海里想的是到瑞士去旅游,但心理价位是10美元一天……

可爱的印度保安。每次见到我都要我给他们拍照。

貌似印度人很喜欢拍照,走在大街上,看到你拿着相机,就会有一大群人自发的站到一起摆pose让你拍。

22

孟买街头警察的巡逻车。也有可能是军队的吧。可能因为经历过恐怖袭击,所以孟买热闹点的地方都有这样的小装甲车在巡逻。不过这车造的山寨气息十足,能不能有防弹的效果很值得质疑呀!

23

要说印度的经济,让我从孟买的一家家路边小店说起。

24

这是孟买,乃至全印度城市人民的主要购物场所——小型零售点,这个概念涵盖了从小摊贩,到小杂货铺再到小门市店这样经营特征的零售场所。这种小零售,占到了印度整个零售行业的96%。所以在印度,你首先是见到家乐福,沃尔玛这样的大型国际连锁超市;再来,你也很难见到苏果,可的,百利这样的连锁社区超市。
虽然印度现在也有高端的mall,outlet,在班加罗尔等少数地区也有大卖场,但在日常零售上印度还处于杂货铺的阶段。

1,占地面积小。上图中的店面在孟买已经算很大,大部份的杂货铺和门市店就几个人宽
2,什么都卖,从烟草到纱丽到饼干到烧菜用的油盐
3,所有权很简单,都是家族经营,妈妈白天看店爸爸晚上看店,儿子女儿放学后来打杂,十年后儿子女儿看店……
4,都逃税,只有现金交易。别小看这些杂货铺,有些铺子可是有电子记账和库存管理系统的哦,但人家就是为了逃税,只收现金。
5,都无完全合法的营业执照。这孟买,你真想办个执照,估计发证的人他们自己都搞不清楚有哪些流程!所以这些小店大部分是无照经营,执法队来了,塞点烟和吃的,夹点钞票在里面就可以了。

以上几点我们老中应该很熟悉吧。我们同行的老外都觉得很神奇!我说我们小时候去打酱油的铺面也就是这样的。

25

这张图片就是很典型的印度杂货铺啦,大部分的印度人都是在这样的店里购置生活用品的。现在肯定会有很多宣传片,说超市在印度越来越普及啦云云,那都是宣传,宣传。印度零售业的基础还是这些杂货铺

1,为什么店铺店面小:因为孟买,还有印度其他城市,都是高度拥挤,寸土寸金,开店的人都是小本生意,自负盈亏,只能负担得起这么小的铺子

2,为什么什么都卖:买方的需求尚在追求门类繁多,随时随取的阶段,未上升到对质量,服务,店面环境和摆放合理等的追求上。

3,为什么都是家族成员在经营:社会文化,各种社会活动还是以家庭为基础的,社会对每个人的分工还未作出彻底的分化。

印度人在窝居方面绝对让中国人自叹不如,人家是真正的四世同堂,奶奶爹爹叔叔姨妈舅舅老丈人全窝到一起,住在100平米左右的“大套公寓”里……因为每个人自己独立出去找房,是绝对负担不起呀!

4,为什么都逃税:谁不逃税谁是傻子!
说到税,印度又是个悲催的国家。印度农村地区的人是不上税的,对,农民和地主富农等都是不上税的,所以全国的税收都集中在城市居民的收入消费上了。这样一来,城市人的负担就重了,城市人口本来就少,还要去负担农村的税收,大家想想就更不愿意交税了,所以大家都逃税,搞到最后现在印度只有总人口3%的在交税,

3%的人交全国的税……

5,为什么不办照:都逃税了谁还办照?

要想真在孟买半个开店的批文,你要跑遍各种工商财政税务卫生治安宗教……等各部门,批完大概53道条文……有时候,有些部门还真不知道有某条文是出自自己的门下……

我要特别在这里说一下,大家有没有注意到印度的小店喜欢把东西挂着卖?
我们小时候遇到的杂货铺也喜欢把东西挂起来,一是为了省地方,二是为了展示产品。这是从商家出发想到的两个有利的地方。

但你们见过有专门为挂着卖东西而设计的洗发水包装,专门为挂着卖而设计的牙膏包装吗?
从印度消费者的角度看,他们对挂着卖的东西比对摆着卖的东西有更高的认同感!很奇葩的理论吧!而且要注意,那些挂着卖的洗发水可不是和我们派发是试用品一样,是剪开个滴出来用的设计,而是有着一个极其小的盖子,用完之后再盖上!一袋避孕套大小的洗发水还要加个盖子,有够奇葩吧!

以上和以下的文字都不是我和同事臆想出来的,而是印方接待的我们企业经理和孟买大学商学院的教授和我们说的,他们说的原因就是想通过这细微的一点让我们领悟印度市场!

印度人和我们一样,要洗头,要刷牙,但是对于月薪只有300美元的印度中产来说,一拼5美元的洗发水实在是太贵了!(谁让印度自己没工业生产!)所以,除非是一家N口,印度人是不会买我们正常包装的洗发水,沐浴液和牙膏的!他们的购买能力只允许他们买最最最最小量的包装!
对大多数企业来说,那种套套大小的试用装就是最小的包装了,于是联合利华等在印度就开始卖试用装!但还是不行,因为即使是这么小的一包,印度人也不会一次用完,而是要分n次!!对分n次用完一套套大小的试用装包装!
如果用剪刀剪开不能封口,那下次再用的时候里面的剩下的就干掉了,就浪费了,所以印度市场立刻产生回馈,在试用装上加个小盖子,每次洗头时就滴几滴出来抹一下。

据说在几年前,印度人会隔N天才用小袋袋洗发水洗一次头,通常都是比较重要的场合,比如开会啦,约会啦,面试啦,站台啦等等。这时候,向人显摆你洗过头就很重要!所以洗发水要做的很香,很香到你一坐下来,周围的人就知道,嗯,你今天洗过头了,你小子能负担得起洗发水,牛B啊!!

所以,这种小包装带盖的设计终于在印度获得了成功。这种软袋子当然不方便立在货架上,只能挂起来卖啦。所以挂式包装立刻就火了,大家都像洗发水那样做成套套大小能挂着卖的软包装,而顾客买东西也开始先看有什么挂着的,再才看柜台里摆什么的。

于是,小店铺都挂着卖东西
…………
不知各位看完后有何感触,我当时立刻明白当初为什么和印度人谈合同时,他们能为少付几个螺丝的钱和我们纠察了一整天……

第一,我的主意是想描述一种心理状态,而不是讨论脏乱差,是想讨论假设是同等程度的脏乱差,为什么印度的更慎人。
第二,脏和干净首先是一种主观判断,再来是由社会投入决定的。很多时候,我们要在主观满意度和客观投入量间做个平衡,就和设计产品一样。如果为了追求干净,那很简单,每个城市多雇佣20万环卫工人,每人每扫一件垃圾奖励十元,同时采购500台清扫机昼夜不停的扫马路。这样造成的时间和物质的投入,最后还是要转嫁到社会个人头上,恐怕那时大家一看垃圾费涨价这么惊人又不高兴了。
第三,要说脏乱差,中国真比周边国家好不少。这个周边国家可不是只有日本,韩国,新加坡哦,还有蒙古,老挝,越南,柬埔寨,印尼,哈萨克斯坦,塔吉克斯坦,孟加拉国。但我想说,我在越南见识到的各种违章搭建和印尼的污水倒喷,都没有印度那么慎人。

我很尊重我在印度遇到的百姓,商人和学者
但我不认同这个国家会在整体上有长足的发展,更别说赶中超美了。
印度要做的是立足现实,解决实际问题,一自己为本,找到自己真正的方向。
我觉得,不管是印度本地人,还是外国投资,能在印度这种茫然无序的市场上发展起来,都是牛人!特别是像TATA这样从中亚迁居来的家族,在印度如何从无根无基到四处出击的,很值得学习。

哎嗨
很多筒子说我为什么不多放杰宝
这是有客观原因存在的
一来印度很多地方不许拍照,特别是好的地方,里面外面都不许拍。我们去的很多地方是人家公司总部,研究院,生产基地。要么是进门的时候就要交出相机甚至手机,要么是一拍照就有人过来劝你不许拍。所以真留下好照片的场合不多。

二来吗,真有好场景的照片,又全都是你呀他呀和印方人员的合照,总之是公事出去要留个纪念也好回来存底。我的同事都表示不愿上镜,就是上马赛克打黑条也不愿意,纷纷表示要低调。

说到底,毕竟这是公事出去,这年头看照片能看出的信息还是蛮多的,到时候就不仅仅是涉及自身,还有整体利益的东西啦。所以抱歉,基本上要挑出没人的照片,我的相机里一百来张才一张,就是拍了几千张,能拿出来的也就几百张。

再有就是有筒子们质疑,说你就是在酒店里面住了N天,有什么资格说了解了印度呢?
这点上,我要诚实的讲,我在出发前是对印度一无所知!我对印度的认识,在很大程度上来自于印方人员自己的讲解。
印方接待我们的公司算是印度该行业里面数一数二的,和我们中石化电信这样相当的巨头企业。估计是第一次和中国人接触,也是为了显示一下自己,对方为我们的行程安排的特别的细,还组织了很多当地高级别的企业领袖和学者与我们座谈。
更重要的是这些座谈不是发生在办公室和教室里,而是在大街上和车间里。这点我觉得印度的学者和实业家们很好,都是立足现实,不玩空谈主义的,这些教授学识很渊博,总是能用街头巷尾一个个小的事物代入一个很大的话题和我们介绍印度(可惜的是我觉得和印度总人口相比,他们就是沧海一粟)
比如说从为什么印度人喜欢穿金戴银,就能联系到印度的历史,联系到社会文化是如何影响印度人的投资观念,再联系到这种观念对现代印度经济的影响。
所以在印度这个半个月,我们相当于天天在听印度版的“百家讲坛”,我现在这里发言的,也大多是对那些印度学者们言语的整理和精炼,个人觉得这对于深入浅出的了解一下印度还是有些裨益的。

PS:估计这些学者们血脉里面继承的就是印度的“讲经文化”,这恐怕是为什么印度历史上一直是宗教国度的原因之一吧。

我下面连发几张我们在印度吃的东西

26

27

28

我们的文化,讲究以食为天。对中国的游子来说,在外旅游的一大快感就是当地的食物很对胃口,既能体验异域文化的新奇感,又能大快朵颐;相反,最大的失望就是当地的饮食难以下咽,转而只能投靠当地的中餐馆,继而发现这所谓中餐馆弄出来的口味也是对不起祖宗,最后只能打开战略储备——方便面!
在一个地方吃的好不好,直接联系到我们中国游客对异乡的观感。那些有特色可口美食的地方,我们印象就会好一点清晰一点;而食难入口的地方,我们感觉就恶劣不少。

世界各地饮食文化不同,就像中国各地也是吃酸吃辣不尽相同,用自己的标准揣度别人的文化确实是有些不公。我们周遭的很多国家不吃米饭,或者采用烹饪方法我们看来比较奇葩的米饭,不过如果能接受文化上的不同,我们还是能发现很多当地特色的珍馐,就像土耳其烧烤,印尼的荷叶海鲜饭。到最后,我们就不用求助于方便面君,也给自己的旅途留下些回味飘香的记忆。

印度是个很倒霉的国家,得益于其在卫生条件上恶名远扬,该国的饮食文化通常直接被剔除到了选项之外,很多短期旅行的人们连尝试一下印度食物的想法都不会有。

我就在想,世界上脏的国家很多,非洲东南亚都有同胞在那里吃过东西,怎么印度的卫生问题就这么突出呢!原因可能在于水!恒河水在大家印象中已经是不言而喻。水是生物生存的基本,如果这个不能保障,那么任何烹饪卫生都无从谈起了:就算你最后要求助于方便面君,那也要用印度的水泡开吧,或者你想求助于麦当劳君,但麦当劳的生菜也是用印度的水洗的吧!

想到这里,众人不免对印度的饮食卫生感到绝望,尤其是在听闻了诸如洗澡时呛水也会导致拉X的离奇事件后,对印度饮食卫生已经从绝望上升到惊悚了!

PS:我在印度半个月,洗澡经常呛水,用酒店的水刷牙,未曾腹泻过。但酒店里面确实提供了瓶装水,如果对酒店净化能力和自己的肠胃没信心的话,还是用矿泉水刷牙吧。

印度酒店本身也在出于转型期,很多设备也在慢慢更新,以前老的净水设备很多真是有问题。我们一位老前辈N年前去印度,出于大无畏的精神用酒店的自来水刷牙,结果真倒下了。现在不拉,不代表以前不拉。当然,反过来说,以前拉,不代表现在还会拉。我希望以后去印度住在酒店里不用天天拿矿泉水刷牙,但事实是这在相当长一段时间内可能并不是每个酒店都能做出这样的承诺,特别是中低级的住宿场所。

我们这次在出行前,几位核心的同胞就做出承诺:
1,绝对要吃印度菜,杜绝方便面,而且一定要在路边找馆子,不在酒店里,少去高档场所。
2,不去景点,有空包车到处随便晃晃,想停哪里就停哪里。

我们坚定的认为自己肠胃的抵抗力应该没问题,就算要拉,也能把免疫力拉出来,有啥好怕的?我们小时候也是手上边玩着泥巴边吃包子,不也活过来了。

在尝试了不少店后,我们现在已经摸出孟买饮食的梗概了。
1,过火的东西都没问题,哪怕是路边摊的。前提是拿东西给你的手和餐具要干净。所以我们去街上吃小贩的茶点和果汁时会自带容器,这样略显怪异,但为了保险起见。
2,我们从有保安的饭店,到装潢不错的店,再到当地人推荐的特色店,再到看起来还凑合的店一级级往下试,前面这几个都可以。一直到某晚的夜市大排档我们才没挺住。
3,我们除了大排档那次腹泻估计是和卫生有关,唯一的另一次腹泻是因为咖喱太辣,而且配着生洋葱吃太刺激肠胃了。
4,一位法国人腹泻是因为胃痉挛,他转机加上丢行李,两天没睡觉,下飞机时肠胃就不舒服,落地第一顿上咖喱鸡,饭后就趴了。还有一位仁兄是喝路边的甘蔗汁,但用的是小摊的杯子,倒了。那小摊的杯子基本不洗,就在边上一筒黑水里涮涮,所以这位仁兄倒的太合理了。还有一个是自找的,非要喝才从母牛体内才挤出来的牛奶,几小时后狂拉……以上这几位都属于特殊情况。

继续说印度的饮食啊
宴会上那些就不说了,没有典型性,实际也就和正常西餐一样没特色。

日常里印度人吃什么呢?
面饼蘸酱,或者面饼夹着菜糊糊蘸酱,或者面饼夹着肉和米蘸酱……
我们在印度街头吃的最多的,是种叫Roti的面饼,我们戏称为“裸体饼”。一则,和Roti的发音相机,二则,很多同仁们表示印度的酱不习惯,饼口味还可以,所以我们经常啥都不夹不蘸,这白花花的一张大饼真可谓“裸体”是也。

29

这饼,有的店做的焦,有的店做的硬,有的店做的软,有的店做的酥,好像就和我们的大饼烧饼一样是个统称,至于能做出啥味道要蘸什么吃就靠创意和兴致了。反正我们吃过最难吃的裸体饼是在对方公司的食堂,就是硬馕饼,没味道还烤胡了;最好吃的是在家路边店吃的,软软的,略带奶香和甜味。

在印度吃饭,你会遇到三个不习惯的地方
1,没有肉
2,全是糊糊,以咖喱为主
3,没有绿叶菜

在印度吃饭,餐厅里会给你的菜单,通常会分为两部分:vege,non-vege
素食和非素食
通常素食占到了菜单7成以上的页面;非素食的选择就少多了,一般就是鸡肉和蛋配不同口味的糊糊,好的店会有鱼,更好的店会有很多种鱼配不同的烧法。牛肉和猪肉是绝对没有的,羊肉的份量少的可怜价钱却不菲。
所以在孟买吃饭,我们一群大老爷们的痛苦在于找不到肉吃!在我等看来,鸡肉一般属于休闲性质的肉,而蛋更就不是荤菜。在对方公司的食堂我们发现,除了我们几个中国来的大老爷们外,印方从男到女连吃鸡肉的都没,全都是糊糊,或者是各种烧法的大饼夹土豆。对方接待的人常和我们抱歉,说因为平常食堂没人吃肉,所以他们今天特地为我们去买鸡肉去了,由于厨子没怎么烧过肉,要摸索一下,所以等午饭时间会要长一点……

因为没有烧过肉,所以午饭准备时间要长一点……
我们非常感激印方的食堂,
然后哥几个扭头合计,每天晚上一定要找到个吃肉的地方,就算是只有鸡肉也不能是糊糊状的,得是鸡块!而且除非万不得已,不去麦当劳和肯德基。

30

在孟买的饮食,大多数是以上面这张糊糊的形式出现。这糊糊里面可能烧的是肉,也可能是素菜。这印度人对于素菜,没有我们这样清炒的概念,从豆子到花菜,从叶子到根茎,全都切碎煮在糊糊里面。这样的烹饪,对食堂和饭店倒也方便,所谓不同的菜,就是同一个糊糊煮不同的东西,或者同一样东西用不同的糊糊煮。所以开始的时候,我们上菜后会比较气愤,明明我点的是A糊糊,你点的是B糊糊,怎么我两的区别仅在于你比我多个萝卜,而我比你多个花菜呢?!

很多国内的同胞会把这种糊糊,一眼见到后就统称为咖喱。这算对,也不算对。糊糊是用香料熬制的,咖喱只是香料中的一大类。糊糊也还有其他的味道,比如有的是茴香味,有的偏薄荷味,有的类似我们红烧的汤汁,有的吃起来类似罐头蘑菇汤,有的会加芝士在里面,有的会加香叶在里面。

我们开始的时候吃糊糊,就和我们喝汤一样,用勺子,瓦出一块咖喱鸡和一勺糊糊,放入口中。我们后来和印方熟悉后,也入乡随俗了,大家都用右手(注意,用右手)抄起裸体饼,把糊糊和鸡肉伴开来,顺便蘸着吃饼。

而我们在大街上看到老百姓们都是这边右手收完钱,然后就用手指在饭盒里搅伴糊糊,然后开吃……如果有米饭,就是手抓饭。当然,洗手这个过程是忽略不计的……所以在孟买很多吃饭的地方,还会有公益广告,劝诫大家吃饭前要洗手……

我们后来还是找到了“以正确方法”吃肉的地方
印度餐厅烧肉,是用烧烤的方法,把鸡肉和鱼肉做成小块,叫做Tikka

31

当然其他吃肉的方法也有,但我们觉得Tikka是最靠谱的了。我们没人每天都会点不一样的Tikka。这个Tikka,翻译成英语,应该是Fillet,菲利小鱼排。这印度菜单,和很多印度街头的英语拼音一样,说的根本就不是英语,而是用英语拼写的印度语。我们经常看到菜单上会有念起来诸如“瓦拉哈塔雅吗母达”的玩意,所以看菜单等于不看,还是得问人。
用tikka烧鸡肉不好,容易老,但是烧鱼口味很佳。我们吃过再平庸的店,鱼Tikka口感都很嫩,火候上既入了炭火味道又保留了鱼的鲜美。孟买是个海边城市,所以会有各种新鲜的活鱼供应。我们在孟买的主要荤菜就是海鲜,各种鱼和虾。如果各位有机会去孟买,去打听当地做鱼好的店,去吃苏眉鱼。

最传统的印度饮食叫Thali,就是一个大铁盘子上面盛放了米饭,馕饼,酸奶,香料还有各种糊糊。

32

到今天,孟买街头的特价午餐就是Thali的都市进化版,依然包含了米饭,酸奶,馕饼和一两种糊糊,但包装可能是饭盒,小资点的用荷叶。
在乡村地区和传统的店里,Thali依旧保留着它大铁盘的原貌。
Thali准备起来颇费时间,所以强调效率的现代餐馆都渐渐放弃了。
传统文化和现代社会的效率文明,不论取舍哪一样,结果都会有些稍许的遗憾吧。

最后说一个印度的甜点,我们称为甜球球,真名叫Gulab Jamun

33

这玩意我们第一眼看以为是茶叶蛋,吞入口中后大吃一惊,有些同仁因为口味上翠不及防吐了出来。
因为这个小球球太甜了!味道就是大白兔奶糖那样加强十倍!
我现在还不清楚这个小球怎么烹饪的,会这么甜!看原料,像是米糕,放在汤水里面煮。初入口时,有薄荷味,然后瞬间就像一颗富含奶糖的炸弹在嘴里轰开了,一股浓郁的奶甜味充斥了口腔。
这小球我们开始吃的时候不习惯,太甜了,有些腻,但吃了几口后就觉得欲罢不能了,越吃越想吃。吃完几个后,嘴巴里面满是糖,觉得黏黏的干干的,但口水依然止不住的流。
大家有兴趣的可在国内的印度餐厅问问有没有这个,然后试试。

34

我们从这张照片,来讲开印度的宗教

当然,我对宗教了解很少,对印度教的了解更少。印方曾经和我们讲了一下午印度教的林林总总,结果是把我们全听晕了。

这印度主要的宗教是印度教,大概全国7成的人是其信徒。第二大教是伊斯兰,大概15%的人口信仰,MSL们主要聚集在南部的成奈和海德拉巴德。剩下的就是佛教,基督教,耆那教,祆教(也就是摩罗教,或者说我们武侠小说中大名鼎鼎的“日月神教”)甚至犹太教,以及其他各种我不知道该怎么翻译的宗教。总的来说,印度是全民信教的,不是信这个,就是信那个,基本上是信印度教。

我们都知道佛教是从印度传来的,筒子们会问,为什么佛教在今天的印度不算主流呢?
这个呢,是历史原因。印度教是历史上存活至今最为古老的宗教,像是佛教和耆那教都可以算作是古印度教的延伸。这个释迦摩尼呀,当初创立了佛教之后并不一帆风顺,在他离世以后相当长一段时间,佛教的影响不大,直到阿育王时代,才把佛教定位国教,这样佛教才正式迎来了大发展。不过在阿育王时代,印度教已经树大根深,完全融入印度的文化了,所以佛教以后还是不敌印度教,还有佛教徒该宗回印度教的哦。

回到印度教。大家看如果在印度,随处可见印度教的寺庙,甚至有很多是横在路中间的。在这些寺庙里大家肯定会见到上图中的两个塑像,一个吹笛子的小生和一个总是和他在一起的女子。
他们两个是印度教中的两个很重要的神,吹笛子的是克利矢那,女子是叫拉德娜,也有叫她拉蒂卡的。至于他们俩为什么是神,怎么个神法,很抱歉印度方的人给我们普及了一下午我们还是没弄懂,只知道他们的故事都是记载在《摩诃婆罗多》中,印度语发音是吗哈布拉达,这算是印度教的神话和经书吧。

印度人对宗教那是非常非常的虔诚。很多与我们同行的印度人,都会在遇见寺庙时,询问我们的可不可以让他进去参拜一下,他很快就会出来。有些特别虔诚的,会每遇到一个寺庙都要进去参拜一下,而且不是我们那样双手合十心中默念的那种参拜,而是五体投地的跪拜,每拜一次就要唱一段经文。有一些人不拜,但是会紧紧扒在神像外的柱子上,默默注视着神像长达几个小时,就像注视自己的父母孩子一样,其中一些人看着看着就突然对着神像流泪了。

大部分的寺庙里面,都有僧侣服侍神像。为什么我要说服侍神像而不是说维护呢?因为这些僧侣真的是吧神像当贵宾一样的人对待,每天早上他们要为神像更衣打扫,修理上面挂着的花圈。在我们吃早中晚三餐的时候,神像也要“用餐”,这时神像会闭门谢客,信徒们也会自觉离开。

寺庙内男女有别,只有男性可以在最贴近神像的位置参拜,女性要在一道栅栏外远远的参拜。

我知道在这里有很多筒子们要我上杰宝,但我真没有近距离拍神像,也没有拍摄信徒参拜的场景。在一片唱经声,四肢磕地声和偶有的哭声中,我真没有自信突然掏出相机来猛拍一阵。大多数寺庙内部没有灯光,很昏暗,我如果想拍的清楚就要打闪光灯或者架三脚架长曝光,这在一个人人都很虔诚的环境里显得太突兀了吧。

印度教历史悠远,派系繁杂,衍生出了复杂的神话体系,和费解的修行方式。印度教的一种和尚叫Sadhu,但这和我们概念上的僧侣不是完全一致,这个词还有些代表流派的意思。有一个比较罕见的流派的Sadhu,叫那伽,这群人我们可能经常会在和印度宗教相关的文化媒体上看到。他们穿着裤裆或者干脆不穿衣服,用石灰粉把自己全身涂的白白的。这些Sadhu平日里自己修行,和外界交流很少,他们聚集的地方称为“阿戈拉”。在重要的祭祀活动Khum Mela时,他们就会出来,依旧穿着裤裆或者不穿衣服。这些人传说是武僧的后裔,他们的祖上是开片刀包围印度教神庙的。

我没有在孟买见到过这种僧侣,从网上转一张那伽Sadhu吧

35

我用那伽来列举一些印度教里面离奇的修行。那伽们自认为是毁灭与创生之神湿婆的后代,所以他们模仿湿婆的造型把自己涂成白色的。而且,请各位注意和谐文字,为了摆脱物质世界的困扰,脱离生死的轮回,他们会吸食大麻或者更猛的,剁JJ,烂JJ,铁刃穿JJ来以此看破尘世。

很多不懂印度教的媒体偶尔看到了这些不穿衣服涂石灰的信徒,以为他们就是印度教高僧的代表。其实不是,那伽只是一个很自我的流派,他们才不鸟什么高僧的头衔,只是孤独的行走在大地上完成湿婆的使命。

当然,大多数印度教信徒都是正常生活中的你我他,除了参拜神庙外,他们和我们没有什么不同。等等,有个不同,很大的不同,这是印度教对印度普通老百姓最大的影响

那就是吃素

印度教是反对杀生的,所有从印度教中衍生出来的,就像佛教,都是反对杀生的。印度教的反对杀生,和我们现代的人道主义观念并不完全一致,而是出于一种原始宗教的信仰,既万物有灵论。和我们现在中土的和尚修行一样,不杀生,吃素,不是单一的修行方式,而是为了净心养性,和其他节制欲望的修行,比如不饮酒,不纵歌纵舞,不近女色,是一个完整的体系。在阿育王的时期,他因为信仰佛教,就命令释放鸟兽,汤中无肉,更甚者,百姓不得聚众,因为聚众就会思娱乐,思娱乐就会有情欲之念。

印度教的来源并不统一,而是上古印度大陆上各种原始宗教的综合。在先民的思想中,万物生养,都是取天地精气,所以世间万物,从牛马到草木,再到土地,都是生命。这种对生命的崇拜,通常在上古时和主生育的母性崇拜结合在一起,于是大地就被赋予了女性的特征,称为达伽拉(音译),就和我们说的大地母亲一个概念。所以与地面接触之物,都是大地母亲的发肤,如牛羊,根茎,伤害它们就是伤害了大地母亲达伽拉,而杀伐越多,大地母亲越发丧失活力,最后万物将死寂,仰赖他们的人类也将灭亡。

上面的逻辑很好理解吧
顺着这个逻辑,对于严格的印度教素食主义者来说,不仅牛羊鱼不入口,就是土豆,胡萝卜这样长在地下的也不能入口,因为它们是生在在大地母亲的“肌肤自下”呀,要获取这些作物,就要切开母亲的皮肤,当被视为大逆之举!
但,我们要问,这不能动,那不能动,我们吃啥?
印度教的逻辑是我们可以吃叶子和谷物,因为它们不是直接和母亲相连,而是母亲生长出来喂养我的馈赠。但是和大地相连的茎,还是要考虑下的。

因此,吃素者,按照他们对印度教的皈依程度,也可以分成几个层次
最浅显的就是不吃肉,其他都吃
下一层,是不吃肉,也不吃卵和蛋
再下一层,是不吃肉蛋,也不喝奶,不吃奶制品
再再下一层,肉蛋奶不入口,土豆地瓜胡萝卜洋葱这样张地下的也不入口
再再再下一层,还有?对,还有。土豆地瓜都不吃,也不可吃糖辣椒等口感强烈的,因为强烈的味道会带来欲望

最最最吃素的,不光我不吃肉,我也不可参与导致他人吃肉的活动。比如我看到有人在杀鸡,不可,我要劝阻。如果我的生意伙伴生产屠宰设备,我发现后就是赔钱也不和他做生意。如果我的朋友是军人,是杀生者,我不和他交往。云云
这就是非暴力。
对非暴力贯彻的最彻底的,就是Jainism,耆那教。
耆那教信徒最多在今日的古吉拉特邦
而圣雄甘地出生在古吉拉特,他的母亲是耆那信徒
……
所以大家了解到为什么甘地会发起非暴力不合作运动了吧。

大家不要认为耆那教信徒都是苦行僧一样破破烂烂的。其实今天的耆那信徒是印度最富有的族群之一,他们中很多人从历史以来就从事商业活动,并领导古吉拉特邦成为当今印度商业活动最活跃最富裕的邦之一。所以诸位,如果有机会到印度西北部谈生意,对方很可能就是耆那教中人,为了气氛融洽,请一定要表现出自己清心寡欲,压抑自己肉食者的欲望,万万不可在谈判桌上一个饱嗝然后一股浓浓的红酒洋葱炒肉的味道滚滚而出……

还有一点,每个宗教体系中都有例外,而操行例外之人就被视为异端,虽可容忍但不可接触。
有些异端是神圣的,比如,巫医。为什么,因为很多时候为了治病救人,要用药下刀,难免要违反宗教信条。比如,有味草药,就是之物的根,药到可治人,但采药伤地灵。若为教条而看同胞活活病死,肯定不是善行。这些巫医,就是宗族中的特定家族,对用药采药的术法,只在父子间相传,以确保医术和药源的神秘性,这样既可以救人,有不会让大家滥采药用植物继而伤及大地母亲。

有些异端是低贱的,比如剥皮者。人总要穿衣服吧,在冷的地方要过冬靠扎草鞋没用,得有动物的皮毛可以御寒。但要皮毛,必会杀生,所以剥皮杀生的职业就有特定的一群人从事。这些人天天在血污中生活,在其他信众严重是异常污秽的人等,所以除非每年特定的交易时刻,平时不愿意和他们接触。这些人猎杀和剥皮制衣的手艺,出了宗族之子,外人不愿继承。

慢慢的,有一些人,不与群体接触,却变得神圣,受到敬仰和祭祀;而另一些人,不和群体接触,却被排斥和隔离。他们的身份和地位都在子嗣间代代相传,久而久之,就变成了种姓。经过文化的发酵,还有出于政治制度的压制,这变成了一种制度。

这就是种姓制度的根本由来。

种姓,宗教和社会职业需求,是三位一体。
说印度是种姓国家是对的,也是错的。因为种姓脱胎于印度教,按理说只要你不是印度教徒,就不会受到种姓问题的困扰。

错!

宗教永远是现实的说辞。现实需要贵者,你不贵自贵;现实需要你贱,你则无法逃脱。

当一种东西变成了制度以后,它形成的原因就不重要了,总会有人根据现实的需要,杜撰出新的理论。以前,种姓是因为职业,后来是为了社会和政治地位,到了英国人带来了殖民主义的时代,则是变成了财富和资本。

所以说,本质上,只要你能混,能赚钱,能在社会地位上向上爬,同时能安抚话语霸权,你就和高种姓是比肩的了。比如TATA集团的创始家族是拜火教,当年是作为难民逃到印度的,但现在没有人认为TATA家族的人是不可接触者吧。

相反,如果你背后的宗教是比较穷的,比较大众的,比如伊斯兰,你就是跳出了印度教的种姓轮回,人家见到你还是:哦,你这个低种姓的,成了MSL也是低种姓的MSL。
你就是把带有种姓记号的姓名改成约翰,改成比尔,信了基督教,也不行。人家一看你这人样子就是低阶层的,查一下祖宗三代,果然被抓个现行!于是继续,你个低种姓的基督徒%…………

当然,在一个社会阶级固化很严重的社会,如TATA集团创始人那样想一步步向上爬,肯定不会像在今日的开放社会那么容易,我想,这点经常看我们老祖宗史书的同胞们应该有体会,比如金辽元清史和东晋十六国时期那些事。个中的艰辛和辛酸,甚至莫名和机缘巧合,只有当事人自己知道,而且人家肯定不会把这个写在公开的家族史书上。

反正,在印度独立之前,英国人是把当时印度航空的所有飞机转给了TATA家族,而不是新印度政府。

种姓这个问题很难谈的清楚。作为当事人的现代印度人,这肯定是个伤疤,肯定不愿意和陌生的外国人谈;作为外国人的我们,始终是雾里看花,怎么也搞不清它的由来和所以然;作为印度政府,他们的态度肯定是基于正面的态度承认一些历史错误,并尝试改革。但我们知道的,因为印度政府的低效和令难出德里,很多改革不是改成了半吊子,就是完全背离了改革的初衷,反而南辕北辙的弄出更大的问题。

比如,独立后,印度政府为了改变种姓制度,先进性了一次人口普查,发现全印度16.2%的人口为最低种姓和不可接触者。印度政府把他们称为:受压迫者。
结果不知道为何,这些人在文件和媒体中被统称为了达利特, Dalit,也就是传统印度对不可接触者的称呼。这下好了,不仅一个歧视性的词语没被消灭,反而被政府赋予了新的含义,连原先好歹还有种姓的一些人也和不可接触者算在一起了。

达利特啊,达利特,原来只是我们地主老爷们这么侮辱的叫你,现在可是有政府官方文件的哦,你就永远是达利特吧!

总之,在种姓改革上,印度政府总是会做出这么奇葩的事情。

种姓制度异常的复杂
1,从历史上来看,因为印度历史多为口述,缺少文献,对于种姓制度的产生和发展,现在只能从经书和其他国家涉及印度的文字中拼凑出个大概。
2,在英国殖民时期,出于统治需要,英国人为的调整了印度的种姓制度,有些较高的种姓被调到了低种姓,有些较低的则被划为更高的种姓,并在总体上拉开了每个种姓之间的社会距离。因为印度历史上频频被外来统治者侵占,我们可以相信许多统治者出于自己的政权考虑都会调整印度的种姓制度这个工具,导致今天的种姓系统和千年刚诞生时的涵义相差甚远。

3,种姓制度类似我们中国古代的户制,包含了社会地位和社会职业的固化。就像中国一个户,比如兵户,会有很多高低不同的家族构成一样,印度种姓制度也是如此。我们知道,常识上印度的种姓有四大层,比如婆罗门,首陀罗,伏舍云云。但是每个大层内,又包含数千个小层面的家族,如果放在政治层面上考虑中央性和地方性,问题更复杂。
这个东西也没有必要深究,就像我们汉晋时期的讲究名门一样,我们可以把高种姓的想象成这样的名门,名门之间也有牛的和一般的,有当权和失势的,就像我们的谢姓家族,王姓家族,司马氏,公孙氏,恒姓家族这样这样。

4,由于种姓制度的继承性,造成了跨阶层不通婚,呈现出基因隔离和姓氏隔离。所以说看长相看姓名就知家族,谁让你爹你爷爷千年来都是这个种姓阶层的呢?

5,职业的不可跨越,由于一直以来很多职业属于不同的种姓,他们之间是避免接触的,或者是有尖锐矛盾的。种姓内的人永远从事类似的职业,而种姓外的人永远不会参与这个行业。这又导致现在印度很多职业是和种姓挂钩的,比如工程师都是出自某种姓,教师都是出自另一个种姓,搞文宣的是某个种姓,做医生的祖上是另一个种姓,经商的是这类人,当兵的是这类这地区的人,而扫大街的依然还是不可接触者。这中分层对印度现在企业的管理提出了很多挑战,比如,搞财务的和搞IT的两类人出自两个不同种姓的系统并坚持互不接触,我该怎么让他们一起开发出一个财务管理软件呢?!

按照我听印度教授的说法,四大种姓阶层加一个不可接触者这样的分类十分的笼统,而且是基于印度教传统,和当今社会的发展并不一致了。今日印度连同不可接触者群体,可以分为12个在职业,人种,姓氏,社会地位,经济收入和生活地区者几个层面上有显著隔阂性的群体。当然说道这里,人家就不愿再深入的讲下去了,毕竟家丑不可外扬,讲的太细,我们都能分出来周围的印度人属于谁谁谁种姓的,反而会挺尴尬。

种姓制度深深的割裂了印度社会。在同等级的种姓间,也或者因为利益或者优越性的见解,发生暴力冲突,称为cast war。这在印度农村很普遍,就像张家人和王家人械斗一样,在印度的电影题材上反映的很多,形成了印度特有的依托种姓制度存在的帮派文化。

种姓制度是有传染性的,即使不属于印度教信徒,但在印度教站主题的社会文化熏陶下,也难免继承种姓文化。今日的印度政府官方坚决反对种姓制度,并通过鼓励教育和就业的方法,力图改变低种姓和不可接触者的社会地位。锡克教和祆教等其他宗教群体也先后声称绝对放弃和反省种姓制度。
但千年之冰非一日之寒,延续千年的劣性文化,在沾染了诸多历史禁忌和既得利益后,哪里是这么容易撼动的。
在孟买这的大城市,消灭种姓制度取得的成果最好,虽然买你对低种姓的人,高种姓的人心里会咯噔一下并开始减少接触,但在言语上并无公开的诋毁和侮辱。虽然在就业上依旧面对不公和必然的歧视,但低种姓的群体在城里里更能获得认同,毕竟这里和国际接轨最充分,经济能力和工作业绩才是现代社会最关注的。
可是在乡村,一切就和百年前一样,低种姓和不可接触者被限制获得教育,医疗和就业资源,很多人依旧在贫困和低贱中生,在下跪和辱骂中或者,然后死去,把这种贫困和低贱继续留给他的下一代。
很多人想逃离印度教,摆脱种姓,于是投奔了声称放弃种姓制度的其他宗教,比如锡克教。但让他们大失所望的是,那些在媒体上对贱民们热泪满面的锡克群体,却在现实中用冷漠和隔离来迎接他们,一切就和印度教的高种姓人一样。在今天,包容低种姓和不可接触群体最多的宗教是佛教,目前印度9成的佛教徒是源自印度教改宗的受歧视群体,并衍生出佛教一种新的宗派–新佛教。
还是说TATA 集团的例子,他们发现公司的中上层全是高种姓人士构成的。于是当时TATA集团下令,全公司要有50%的职位要预留给低种姓和不可接触者。但收效甚微,因为后来TATA发现低层人士资源太有限,即使他们有工作能力,也没有途径知道TATA在招募,或者来应聘了,但在招募过程中被莫名的“筛选”掉了。为了贯彻种姓平衡,TATA高层开始直接跨过人事层,专门招一些低层少年在公司内从小培养,在他们适合工作后,再直接空降安插到高种姓群体中。

如果我们在种姓隔离上,再加上宗教隔阂,和每个国家都有的地域歧视,以及新时代的拜金主义,那印度社会现实中真是充满了各种隐形的墙。
我说两个例子吧
第一个是我们了解到一个印方接待人员,和他女朋友认识7年了,我们调侃问他什么时候结婚,谁知他顿时惆怅了:我是MSL,但她是印度教的,所以我们没让我们的父母知道我们的事情……不过好在我们两家都比较有钱,所以……so,well,so……

第二个是我们的一位陪同,有天回酒店的路上突然问我们,想去看贫民窟吗?我们说很好奇,于是他带我们去了/然后带我们去了一个他说是不可接触者的聚居地。我们就问他,这些人是哪里来的,都是祖上就在这里生活的吗?
他回答,不不,他们不是孟买人,是南方人
?那么什么是南方人呢?
他继续回答:马尔杜斯人
?那就是现在的成奈?
他说恩,对,可以称他们是成奈人
?是不是成奈历史上有那么多不可接触者,你们为什么叫他们南方人?
他继续说:那都是因为他们信了伊斯兰,你们知道吗,南方人,那些MSL,他们就是这个样,达利特!他们是officially scheduled(这个不知何解)。你们了解这点就足够了,不要去想太多,接受这个,别问太多,你们在印度生活和做生意就会很舒服。

好了,在进入下一个话题之前,我先给大家分享一些关于印度的统计数据吧。资料来自于米国CIA的World Fact,嗯,中情局的世界概览……都是公开的信息,大家可以谷歌CIA world fact India

印度的土地面积为世界第7,人口总量为世界第二,人口密度为世界第24,人口增长速度为世界第102(黑非叔叔们太给力)
印度50%的人口在25岁以下,65%的人口在35岁以下
贫困县以下人口为22%
全国失业率是7.8%
男性人口寿命预期是67岁
女性人口寿命预期是72岁

印度人口前十位的邦为:北方邦(这也是世界上人口最多的国内行政区划),马哈拉施特拉(孟买所在邦),比哈尔,西孟加拉,安达拉(海德拉巴德所在邦),中央邦,泰米尔纳都(成奈所在邦),那贾斯坦,卡尔纳塔卡,古吉拉特

识字率最高的邦是克拉那,94%
识字率最低的邦是比哈尔,64%

主要宗教构成为印度教,伊斯兰教,基督教,锡克教,佛教,耆那教

印度总共有1652种语言和方言,其中23种被列为官方语言。
全国41%的人说印度语,是说得最多的官方语言,其他前十位的语言是孟加拉语,特鲁谷语,马拉提语,泰米尔语,乌尔都语,古吉拉特语,坎纳达语,马拉亚拉姆语,奥利亚语。

2009年期婴儿出生死亡率为千分之52,比起98年的千分之74已经是大为改善。

全国最大十个的城市是孟买,德里,班加罗尔,海德拉巴德,艾哈迈德巴德,成奈,加尔各达,叙拉特,蒲内(这城镇离孟买150公里,开车4小时,快和孟买融合了),斋浦尔

印度没有统计全国的人种,所以全国多少人是属于这个雅丽安人,多少人是那个人,官方上是不知道的。但印度统计过全国的种姓,还有基因。

但据信,印度次大陆上共有2000多个不同种群的人,基本上世界上你能想到的宗教在印度都能找到信徒,基本上世界上四大主语系(印欧语系,达罗毗荼语系,南亚澳语系,缅藏语系)的分支语言在印度社会中都有人说,此外还有两种印度特有的隔绝语种,马哈拉斯特拉和布鲁沙其语

再说说种姓

前面说了印度如东周列国,中国的分封制度让秦结束了

那么中国的种姓制度哪里去了?

中国是有种姓制度的,现在日本韩国和东南亚都还能看见种姓制度的影子,这都是当初从中国传递出去的。
很多筒子,我猜到,会说文革,剪掉了流毒的尾巴。
但在我看来,中国的种姓制度,在宋前就流产了
种姓是什么,就是两汉魏晋时期的士族。王导谢安,都是士族代表,但他们是好的代表。曹丕推九品中正制度,那就是推荐体系呀。你是士族众人,大家都推你,哪怕你是饭桶,也能混个一官半职。你是寒族,那要出头就难了,即使才华出众,但无人举荐,你就一辈子吃糠皮吧。雄主如曹操,因为是宦官之后,还总是被其他士族大臣指指戳戳。士族和寒族,地位,职位,财富和基因都是互不流动的。士族占着大坑,还特别鄙视真本事,因为在他们看来人要有本事呀,那是迫于生活压力,才勤恳苦读,这样一来真才实学反而成了不上台面的东西。大家可以看一本美国的小书,叫恶俗,英文叫BAD,里面就描述了“看不见的阶层”是如何的,他们是坐着考斯莱斯但看不懂时速表的人物。

那么士族有什么呢?他们有“风雅”,所以你看那是有很多诸如“清谈家”的人物,比如被司马懿弄死的何晏。但,各个清谈的士族们,压迫下内部的寒族没问题,是没有办法能搞定塞外弄真刀真枪的胡族的。清谈到最后,没有本事的士族就被胡人们咔嚓了,而真有本事的人或者自立自强保存汉室,或者投奔胡人出谋划策。

要知道胡人是很讲效率的和投资回报的,进入中原后看你祖上当官,于是看得起你平日里封官发银子。但若哪天把你叫入大帐咨询你明年是该出陇入川还是减税养民,结果你上古先人云云清谈一番,来火的单于大汗们立刻就脸一黑,把你推出去砍了。

就这样,南北朝抄一遍,五代十国抄一遍,辽金元抄一遍,一直到乞丐皇帝老朱上台,不实用的家族和人物也差不多抄完了。还剩下点的,明末抄一遍,民国抄一遍,本朝抄一遍,还活下来的就真是有本事的了。

印度也曾有过火红的年代

印度人一直坚持,他们在1990年以前是一个社会主义国家。
这有很意思,也很难理解。今天,西方人一直很困惑,中国是一个社会主义国家吗?如果不是,他为什么姓共?如果不是,它的市场为什么又姓资?
印度的情况也类似。和我们正相反,它在政治上实行的是议会民主制,无党禁报禁,但在经济上采用的是当时苏联的计划经济制度,是姓社的。这很好理解,开过初始,印度和中国这样没有治国经验的国家,肯定是采取拿来主义。在当时,二战后的苏联在亚洲的影响力如日中天,计划经济的制度也确实证明了其在推进落后国家振兴上强大的爆发力。要想在一穷二白,啥基础都没有的国度建立起工业体系,就要集中资源,重点突破,先解决有没有的问题,再去讨论好不好富不富。

50年代的印度,那就是第三世界国家的明灯,发展中国家的骄傲,不结盟运动的领头羊,方方面面看印度都当之无愧是所有后进国家中的老大。即使是在朝鲜战场上震惊世界的新中国,也对印度毕恭毕敬,两国一度以兄弟相称,尼赫鲁和老毛互相夸耀对方是一代英豪。经济上看,印度有着英国人留下的完整铁路系统和部分工业,底子最好;政治上看,英国人也给印度留下了议会民主制度丰富的执政经验。能把千百年来一直四分五裂的印度纠结在一起,不管手段和结果如何,尼赫鲁凭此就是大功一件。全印度人民在尼赫鲁的带领和鼓舞下,大干快上,修水坝,兴学校,铺公路,建三军,好像强大富裕的明天指日可待。尼赫鲁的评价“水电站就是印度新时代的神庙”,这话体现出了他本人的自豪,也体现出了当时印度全国蓬勃向上的精神面貌。

但是,但是
好莱坞的转折来了
但是两场战争彻底打掉了印度的威望,让印度人民骄傲的脊梁猛然又塌了下去。
第一场:克什米尔,印巴战争
英国人的印巴分治是个非常荒谬的决定,而印度人竟然以此模式作为标准,欣然执行,结果给自己酿成了长期的苦果。这决定荒谬在什么地方呢?英国人以信仰划分大印度殖民地,穆斯林为主的将成为巴基斯坦,印度教信徒为主的将成为印度。今天的印度,其东南部地区人口依然集中了大量的穆斯林群体,如果以当时的划分方法执行,那么今天的印度次大陆还将少掉南边的一半,也并入当时的巴基斯坦。那样一来,印度就成了汉堡包中的肉,南北都被巴基斯坦夹住,而巴基斯坦的领土则被印度割裂成今天的巴基斯坦,孟加拉和南印度三块。不论是巴基斯坦还是印度,此种领土划分法都是一直莫大的纠结。
当时统治海德拉巴德的总督,是个穆斯林,尼赫鲁给当时去谈判的使者下达的口谕是,不惜一切要让海德拉巴德并入印度!这位被印度教徒包围的穆斯林总督最后是同意并入印度而不是巴基斯坦,印度新政府免去了他的实权但给予了他巨额的财富,据说在相当长一段时间里此位最后的总督都一度是世界首富!
这样一操作,新的问题来了。印度南部并入印度新政府后,当时印度国境内穆斯林的人口比巴基斯坦的总人口还要多!这种情况下,按宗教信仰执行的印巴分治又有何意义呢?但双方还是决定分家,而且在多个地段上分出了问题,分出了呲痦。克什米尔就是个大问题
今天的印度人,当然为了面子吗,会在克什米尔的前因后果上绕来绕去,反正宗旨就一条:我没错,我也没输!
印度输大了。首先是把一个可能的兄弟国家打成了敌对国。今天印度国旗上的绿色还是代表伊斯兰教呀!等于说印巴战争是一个穆斯林和一个半穆斯林国家互相砸场,毫无意义。再者,丢了一半的克什米尔,还丢了其他地方,边境线长期不得安宁。第三,把问题国际化了,冀希望于外界调停,谁知道结果联合国达成的决议对印度不利!于是印度就赖帐,不执行联合国决议,在道义上又输了一阵,还给他国介入留下了很大的空间。
尼赫鲁这个人是个理想主义者,那思维方式就是“我觉得……我觉得……”这样的,如此的人格容易对外界环境的险恶估计不足,就是估计到了也因为互动不良而导致局势恶化。尼赫鲁觉得巴基斯坦肯定会退步,但老巴就是不退步;尼赫鲁觉得联合国是个公平正义的场所,好人先告状,好人定有好报,但实际上人家就是没给你好报;到了和中国纠结的问题,尼赫鲁再犯类似处理巴基斯坦问题时的错误,觉得中国一定会像他的思维那样运行,但中国怎么可能是按照印度人的想法出牌呢?

于是就有了第二场战争:中印冲突

尼赫鲁又把一个潜在盟友打成了敌对方,而且再一次在战役,政治和道义等几个层面上输得很惨。

短期内连输,印度人民被打迷茫了……

计划经济基本上没有改善印度的基础设施,也没有为印度打下多少工业家底。印度的大命根子:石油,在半个世纪内多次把印度经济推向悬崖。1990年,海湾战争,国际油价暴涨,极度依赖进口原油的印度经济再次被推倒了绝壁边上,全国的外汇储备全被油价这个黑洞吸走,剩下的额度只能够支付数日的国际贸易,这等于说印度破产了!

印度开放了,摒弃了计划经济,在几个月内全国的经济发生了颠覆性的变化。
今日印度总理辛格就是当时的财政部长……

印度人的思维,对于管理的一些见解,我们现在还是不能理解。

初来乍到,我们总觉得印度人对现代管理是一窍不通:没有时间概念,没有岗位责任分工,谁干什么,怎么干,干多久,貌似基本是随性所致。

但是,和印度一些高级的管理人士沟通了一下,又觉得印度人对管理上研究的还挺深入,制定的很多方法和规定很有针对性。印度的高层人员,与其说他们是企业家,不如说他们是学者,沟通起来上知天文下知地理,而且态度平和近人。说到一个话题,他们总能引经据典,又结合实际,颇有一种博通古今的气魄。

印度开国之后,对人才队伍的培养一直就很重视。大家可能知道,印度有一个国家工程学院,入取率可算是万中挑一,每年只在全印度招考3000个名额,竞争压力可想而知。进入了工程学院,就和我们以前中了科举一样,毕业后绝对是前途无量。和国家工程学院一样,印度还设立了一个国家管理学院,培养经济管理类人才,和工程学院一样,也是前途无量但人选是万中挑一。

这是印度的骄傲,也却是印度的悲哀。印度在类似这两类学院上投入了大量的补贴,在基础教育的投入上却相对见绌。基于此,印度出现了教育水平上的两级分化,一面是大量的文盲,一面则是高质量的精英梯队。到今天,印度的大学都是以全英文授课,且不论你口音是不是纯正,但要以全英文书写学术论文,这要求可不低哦。大家可以把公司里面英文系的筒子们拉出来,写一篇全英文70页纸,你们产品的行业报告,我估计大部分听到后就吐血请假半日了吧。所以印度人去英美读研究生(不是本科哦,是研究生),不需要考托福雅思,人家看你是印度大学学位,知道你的英文是有高端山寨水准的,至少不是小白。

高质量的人才,相对于庞大的文盲队伍,就是杯水车薪。高端人才没有配套的产业,天天还要和一群文盲打交道,实在是浪费人生!所以,在90年以前,印度的这些人才大量外流,移民欧美。这其实还是很值得的,因为这些印度的外流人才,在90年以后又是投资杀回印度的主力军,就像我们改革开放的初期带头投资中国的都是华侨。

好了,回到正题,这印度人的思维怎么奇葩呢?

人要做任何的事情吧,都会遇到困难和问题:比如我今天要高考,考试很难我复习了很久。不幸高考当天,我病了,头昏眼花,写一题就流一摊鼻涕,痛苦不堪。

从管理来看,苦难分两种:一种是可以争取克服的,靠主观能动性能解决的;另一种是难以克服的,是自然生产,人力难以改变的。
第一种困难,我们称之为风险,也有的称为可控因素。我们现在总是说风险控制吗,这就点明了风险是可控的,是需要精心调配资源去克服的。上面举例中,考试很难就属于风险一类:我复习好,难度对我的威胁就小一点;我复习的不好,难度对我的威胁就大一点。难度对我的危害程度,取决于我自身努力的结果。

第二种,我们称为不可控因素,或者称为限制,英文constraint。这些问题都是天灾人祸和即成事实,对于发生的时间地点和危害程度,我们很难控制,比如上个例子中的生病:我高考当日发病纯属倒霉,病得有多惨纯属人品,对考试发挥的影响很大但难以量化。实际管理中,类似的问题或者是尊重自然事实:比如黄河改道问题,你就得清出泄洪区,不必纠结于如何规范黄河河道;或者是暂时搁置,做长期规划:比如考场感冒,就说明你这段时间抵抗力差,只能靠锻炼身体加强。

所以说啊,筒子们,在风控上来看,交通事故和刮台风是不一样的概念,前者是可控风险,不违章就不出人命,而后者是不定期的自然灾害,突然起风了你的船被卷走了,那属于血霉。

对于美国人来说,风险和限制,也就是可控和不可控是对半开的,人家对不可控的东西分得很清楚,碰也不会碰。尤其是既定事实,比如公司规章,法律制度,国家政策等等,在美国人的概念中就都是不可控因素,至少对个人来说是如此。所以对美国人来说,合同上说6月执行,就依此操作,因为这些属于不可“管理”的“既定事实”,应当“尊重”或者“回避”,不需要在上面花功夫“努力”改变什么。

中国人呢想法就不一样。自从大禹治水之后,中国人脑海中没什么“限制的概念”,时不时的喜欢“欲与天公试比高”,做一些“改造自然”的事情。对中国人而言,天下之事,大多数都是“可控”的,风险只有影响大小而言,没有“不可控”一说。区别只在于成本和时间投入和结果值不值。所以在外国人看来,中国人常做一些“又想马儿跑又想马儿不吃草”的事,因为中国人看来没有什么特别多的事情是不能“管理”的,凡事都是可以谈的可以“控制”的。“管理”得好,“控制”到位,啥都能卖,啥都能干,马儿就是不吃草,也至少能跑两里吧,比一点都不跑要强。真要饿死了,届时再说其他“管理”和“控制”的方法。

印度人的奇葩在于,他们把很多其他人认为是可控的因素,划归到不可控这一栏去了。我们很多认为很基本的要求,在印度看来是uncontrolable,而且既然不可控,那印度人就完全漠视,一点不去花精力去过问。在印度人看来,市场,定价,宣传策略,包装等等,这是可控的。但我们认为这些东西很虚。相反我们认为一些重要的东西,比如周期,耗时,员工等等,印度人认为是不可控的!他也不会去管理。我们估计啊,是因为印度的基础条件限制,导致的一些些痛苦的“既定事实”把印度人虐惨了。我们中国人,遇到这些问题,要啃骨头,越是困难越是要上,因为今天你不解决了拖到以后问题越来越大,届时再解决成本就大了。印度人的态度是甩手掌柜,爱咋咋的,不管了。

我们问一个印度人,为什么他们觉得人工是不可控所以不去管理呢?他的回答是:他今天来上班,明天去另一个地方面试,然后拿着对方的开价来找我涨工资,我不知道他去了哪里,不知道他和对方谈了什么,而他最后也没有去对方上班,这一切当然都是不可控的,我为什么要去过问呢?
…………

如果你能理解印度人这种把很多事情划分为“不可控制”,并且之后就真的“完全不控制”的思维方式,对印度的很多奇景你就能立刻理解了。很多不能想象的奇葩,以这种思维去揣度,估计在印度人的世界观里,完全就不是问题,因为连产生问题的场景在他们的脑中都是“漠视的”,或者“不存在的”。

你会见到在同一个公司里,研究室里几个人非常卖命,周一到周日,每天早6晚十的在赶工。而生产线上,或者旷工,或者A的岗位B在干。你会很疑问,这家企业的管理有没有问题?他的员工到底勤劳不勤劳?他到底有没有完善的问责制度?
答案绝对是有的,只是因为那几条生产线属于“不可控制”,所以印度企业就完全不管理了,而是把所有精力放在那些“可控制的”区域

走在孟买街头,你又会发现,现代品牌的专卖店旁边是小黑屋,高档mall的街对面是贫民窟!你会问,这个城市没有划片规划吗?难道在开店的和购物的看见边上的烂楼不觉得担心吗?答案是不觉的,因为印度人觉得建楼开店这种事情,张三开在这里,李四开在那里,是“不可控制的”,所以没必要去规划。高档店旁边的小黑屋是“不可控制的”,既然如此就应当是被“默认性的”“忽视”的。在开店的和购物人即使大眼睛看见了自己边上的景致这么糟糕,但在他们的脑中,这些小黑屋根本就不存在!

说道规划,孟买这个城市没有一个类似我们市长的职务,现在的市长就是一个荣誉头衔。那么谁在管理孟买呢?一个由很多单位组成的,意见互不统一的委员会。这么多群龙无首的政府官员,印度人认为是“不可控制”的,所以也不需要去想怎么弄出个领导,于是大家都“互不控制”,随便你想干什么就干什么。

说道媒体,大家都知道印度是个传媒开放的国家,谁想说什么评论什么,都可以上舆论吼两下。这个印度节目的评论员,想法和言论完全是彻底的扯淡。我这里想说的媒体“控制”不是国家要对舆论要进行严格的管控,而是行业界对参与人员要有一个基准的业务要求。一个合格的媒体人员你可以上电视就事论事的挑战对方,但不能坐在那里随意扯淡。BBC的主持人,绝对不会随意在电视上问财务大臣“你觉得在苏格兰人们家里是养狗还是养猫?”这种无厘头的问题,否则这期电视节目有什么意义?而印度的电视采访,扯淡非常多,而且离题万里。比如采访一个阿联酋来的企业老总,那个印度记者在机场问的是“你认为某某板球运动员退役后应该赚了多少钱?”这是采访一个刚来到印度,还没出机场的外国企业老总的问题吗?而印度就是这样,你可以在涉及外交的节目上谈自己很擅长烧咖喱,也可以在涉及印巴冲突的节目上谈某位影星快结婚了。因为印度人觉得说什么话题完全是当事人即兴发挥,纯属“不可控制”,所以不需要做任何的规范。

不知道是这种“不控制”的思维导致了印度的乱象,还是印度的乱象催生了这种“不控制”的思维,总之印度就是存在很多无法对接的差异,而且这些鸿沟性的差异也不被认为是一些“很重要的,需要管理的”问题。印度的社会,往往就是一个不靠谱的差异,扯出一片不靠谱的问题,然后很多不靠谱的事情以几何级数堆积在一起,搞得整个国家以一种很随机的,无法定义的状态在前进。

印度通常被人们形容为大象,因为其缓慢和改变方向的困难。但我更愿意称印度是一个电子云,大家了解原子的结构就应该理解。印度社会的每一个单位,就像围绕质子旋转的一个电子,看似各自都有一个固定的轨道,但是如果想知道大家某事某刻在何处做什么,下一刻在哪里?这些问题的答案都是随机的,很难定义的,讲不清楚的,也不必去深究……

很靠谱的精英,和一大群不靠谱的人民。
这让我个人很难对印度人做出一个评价。
我只能说印度的两极分化存在于这个国家的任何一个层面。从广义上的文化,到微观上的个人,都存在着差异。
我可以说我遇到的高水平印度人,那都是牛人。能在这种混乱的国度揣摩出生存和发展之道的人,肯定在才华和抱负极为强悍。但我为他们感到可悲,因为他们周围的芸芸众生和他们相比,请原谅我的语言,那简直有着山顶洞人和现代人的差距。这些孤立的智者,数量如同沧海一粟,却要拖曳着印度这一整只泥足大象蹒跚前行。即使他们的能力和品格犹如星辰般闪烁,砸到这一滩黑水里也就泛起定点的涟漪。
印度现在有很多举世瞩目的成就,但是这些成就相互无法策应,全部是某几个人孤军奋战的结果,难以在全国范围内造成大的影响,在系统上对这个国家的撼动非常的小。

说点开心的吧

农民这个词汇,在现在的中国,已经有一些歧视性的含义了。比如,我们媒体现在都是用“外来务工人员”或者“城市流动人口”这些中性化的词汇来描述在城里打工的村里户,避免直接使用“农民工”这样指代性很强的词语。

但是在印度啊,这个农民可不是我们意义上的农民。我们刚到印度的时候,常听当地人说某地的农民今年和乡亲一起买了50辆宝马,某地的农民今年和家里20多口人去了纽约,某地农民的儿子刚从瑞士上完商学院回来,此类云云。开始,我们很想当然的认为这是印度人在竖典型,造势搞宣传。可后来听着,发掘有问题了,比如我们问当地人,你们说的那个地方有多少农民?他们说大概就30来户吧,所以买了50辆宝马呀!

我们就懵了,那么大个地方就30来户农民?我们中国一个乡里就有几十户了,一个镇子就有百来户农民啦?!
印度人也懵了,我勒个去!你们中国有这么多农民!?

后来我们弄明白了,那些印度人刚才说来说去的是富农,或者有些是小地主,不是佃户!怪不得能全家20口去纽约玩呢!
在印度,这些富农,当然也还是下地的农民,但他们是不干最粗重的活计的,都是些技术工,开开拖拉机,规划一下养殖,买种子交粮食。这些高级农民的日子并不难过,在价格上农产品有国家保护,在税收上村里人不上税,在社会上他们算村子里的半个土皇帝。
在印度这些农民在政治也是有势力的。大家都知道印度是民主国家吗,是要选议员的。而议员是来自按照国土面积划分的选区的,好了,大家算算看:1,印度现在是城市面积大还是农村面积大?2,印度现在是农村人口多还是城市人口多?
两条算来,农村都有优势,所以被选出来的议员们,有农村背景的在人数上高过有城市背景的!农村来的议员当然要为农村说话,当然在政策上和资金上要偏向农村而不是城市!这又造成印度的城市化进程很缓慢,原因就是城里人背景的议员数量较少,各大城市朝中无人呀!!
因为农村人在印度不上税,所以很多印度的有钱人为了避税,就说自己是农村人,然后编纂了半天说自己自小就是农民子弟,现在要荣归乡土等等,简单来说就是哥们以后做农民去了,不上税了!N年前就有个宝莱坞影星就公开宣称自己是农村人,结果掀起轩然大波!全国人民都知道,这不明摆着就是为了逃税吗!

我们就遇到过一个印度人中产,自称家里是农民,请我们去参加他弟弟的婚礼,体验一下最正宗的印度乡村文化。
当时我们那个嘀咕呀!农村啊,农民呀,还是印度的农民呀,那家里会是啥造型呀!城里都这样了,印度的乡下,印度的农民家里,怎么能去呀!

于是就几个胆大好奇的去了
回来后汇报,乃们不去太可惜了,这辈子估计没机会简单这么壮观的婚礼啦!Y忒虚伪,什么家里是农民!家里有四千亩地,就是一地主!婚宴上来了三千七百多号人!吹吹打打的在前,各种喇叭吹的震天响,后面的接亲的队伍不知道排的多远,从他家到女方家,20公里就这么一路闹过去,还不停的有路人加入凑热闹。各种造型的人,一路上载歌载舞,从下午一直闹到晚上。到了晚上,婚宴开始,这时正式的歌舞团才出场,大家又是载歌载舞闹到半夜,旁边有女仆不停的撒鲜花,只有新郎新娘从头到尾直崩崩的坐着。
要说新娘戴的黄金首饰,完全就是黄金圣衣呀!好家伙,据说脱下来都能堆出一个人型了!纱丽上镶嵌的都是真的宝石!估计穿起来估计重死了。

然后我们问道吃喝住的条件如何……
几位一拍大腿说,乃们不去太可惜,偶们总算是体会到商纣王过的是啥日子了……(看来所谓农民其实是农场主)

至于经常发生的强X案,我个人的看法

被压抑太严重……
阿拉伯世界对男女问题管的最严,反而是性侵犯最厉害的地方,男女都会被轮。
不能吃肉,不能饮酒,不能歌舞,不能大笑,男女婚前不能见面……于是大家被鳖到找到个树洞都想插……

把话说的糙一点,试想,生活在一个社会,晚上下班后什么没地方去没事情干,回家想看个电视剧还碰上停电,那一群男男女女能干啥?只能干了呗……如果长期的生活都是这样无事可干只能做爱,再加上国家无生育限制,那就人口爆棚了呗。

如果很无所事事,但是连去找男人/女人干的正常渠道也没有,那就只能乱暴了呗。

所以我们现在社会的各种设施,对分散精力并最终控制人口是有很积极意义的呀!
想进步有夜校,不想进步有麻将;有钱可以会所,没钱可以大排档;年轻可以夜店,年老可以练功跳操;不安分的可以莺歌燕舞,死宅的可以在家联机;男的可以桑拿,女的可以spa……总之每天晚上的生活不可能缩减到只有滚床单这一项。

就是想发泄,但是没钱没机会,还可以打开电脑强撸呢,怎么会有人轻易就担当风险去做采花大盗?

我们还发觉印度人有个不可思议的地方

意淫大家都会,但是突破心理障碍,把意淫转化为行动,还是需要一些“勇气”的!
我们要是在公共场合看见美腿美臀,肯定会多看几眼,但总体不会有人会看着入迷了就当场“行动”吧!

但印度人在动手这点上,好像没啥心理障碍。从乞丐,到街头不明人物,对你看几眼,就手脚摸上来了!
不给钱,就在你身上摸
大街上的陌生人,想和你外国友人打个招呼,手就往你身上“深深的”一摸……

我没接触过印军人员,在街头只见到过警察。印度警察穿卡其色制服,乍看以为是军人,其实不是。

我每天看新闻,当然看不懂印度语,但是能看懂画面,偶尔能听到间歇性蹦出的几个英语。新闻中关于军队的报道很少,即便当时印度和巴基斯坦又处于了对峙状态,新闻中对军队的报道还是不多。在有限的几个和军队有关的新闻中,谈到的大多数是印度的山地师和他们的装备,关于军队架构和训练的内容很少。

就我所在印度的半个月内,我不觉得印度民众对拥军有很大热情。印度人民的热情都消耗在他们的三个神上面了:印度教真正的神,宝莱坞影星之神,还有板球明星之神。

我们请教过印度的“精英”们
他们都很肯定的认为印度的民主制度不会有任何作为,他们都坚信印度要发展,最不能靠的就是政府。
印度的民主完全流产于三个层面:
1, 地区利益差异
2,宗族利益差异
3,派别利益差异
总体上和我们1920到30时期的民国政府差不多。从政党内看,派系林立,大部分有家族和集团背景,互不合作但也消灭不了对方;从地理上看,政党在地方保护主义面前没啥力量,不同区域之间的党派不可能互相渗透融合;在地区内看,宗族又互相牵制,不同种姓的人不会选另一阶层的人呢,同一种姓内的不同家族也不会无私的支持另一个家族的人。

个人感觉,印度的民主制度,除了保证大家都能说话外,没有其他大的作用

(这里有个网友的回帖,很精彩:
由于工作的原因,我去了印度4次,前前后后加起来半年由于,去的时候主要住在孟买的Hirinandari,期间还去过Puna和加尔各答。
1. 对于两国的媒体:大多数的时候都在相互诋毁,中国就不说了,我在Puna看过印度的早报,当天有四条关于中国的新闻:a)头版头条是中国帮助巴基斯坦修建边界设施;b)中国商人在中印的贸易经常欺骗印度人;c)中国的海关出口数据是假的,缔造的;d)北京的雾霾。
2. 对于印度人:个人认为不像楼主说得那么色,对于外国人来说还是很友善的;很假,和成都人差不多(比如请你到他家做客,根本不给你说他家住什么地方);毫无时间观念,minutes意味着几个小时;第一天告诉你第二天可以帮你测试产品,结果第二天别人请假度假去了…
3. 印度的治安:个人认为除了QJ以外,治安还是好过国内,毕竟别人有信仰,晚上揣个万八千在街上不用担心被扒被抢。
4. 印度的脏:不说了,反正很脏,类似KFC这种地方都是千年一锅油的,去一次不去第二次。
5. 上面提到的农村人来城市享受城市人的福利:这是不可能的,印度也有像中国万达广场一类的凤凰城,RCT这些。但是门口都有警察,只有穿得很体面的人才可以进去的;住在Hirinandari的人享受免费自来水,建民就只有路边坑里面的水。很多男人都因没钱无法结婚,在服务业不发达的大环境下,QJ案就很自然了。
6. 关于印度的等级制度:举一个很简单的例子,我们住的地方,居民的等级最高,过了是司机,最低是保安(当然,保安已经很高了,有工作的人在印度都是很自豪的),很多居民的小朋友从小到大就没喊过保安“叔叔”,都是boy,boy的。
7. 银行利息:如果拥有印度国民资格的话,银行利息是很高的,可以达到一年9.8%,曾经错收到我们楼上一家的银行对账单,存款一亿卢比,吃利息都够了。

总之,印度是一个发展与落后并存,精英与文盲并存,几乎没有政府管理的社会,用印度政府的广告语“Incredible India”(不可思议的印度) 真的是一点也不差。)

度的公司,都是巨头型公司。
这些公司和我们国家的国企巨鳄,还有俄罗斯的寡头公司,美国的垄断集团都不一样。如果要类比的话,最相似的应该是日本在二战前的财阀。

印度的大公司,在横向的行业跨度上,和纵向的产业集成度上都十分夸张。这些公司,涉及了从通信到资源到化工到药物到重工到银行到IT到咨询的许多行业。比如TATA,印度媒体广告中4成的品牌和企业,都会和TATA发生联系。还有Reliance,这是一个石化企业,但是街头巷尾都能看到它的手机业务广告。

很难想象,在一个地域差别这么大,商业环境这么差的国家,竟然能出现许多这样跨越全印度的巨头企业。这些公司的崛起,都是在90年印度开放以后,但他们中很多的历史都可以追述到英国殖民时期。在90年以前,印度实行的是计划经济,所有产品凭票供应。在开放以后,印度原先的国有企业迅速衰落,TATA为代表的私企财团立刻走高。

印度的公司,都是巨头型公司。
这些公司和我们国家的国企巨鳄,还有俄罗斯的寡头公司,美国的垄断集团都不一样。如果要类比的话,最相似的应该是日本在二战前的财阀。

印度的大公司,在横向的行业跨度上,和纵向的产业集成度上都十分夸张。这些公司,涉及了从通信到资源到化工到药物到重工到银行到IT到咨询的许多行业。比如TATA,印度媒体广告中4成的品牌和企业,都会和TATA发生联系。还有Reliance,这是一个石化企业,但是街头巷尾都能看到它的手机业务广告。

很难想象,在一个地域差别这么大,商业环境这么差的国家,竟然能出现许多这样跨越全印度的巨头企业。这些公司的崛起,都是在90年印度开放以后,但他们中很多的历史都可以追述到英国殖民时期。在90年以前,印度实行的是计划经济,所有产品凭票供应。在开放以后,印度原先的国有企业迅速衰落,TATA为代表的私企财团立刻走高。
回复
楼主:vendom 时间:2013-02-20 22:35:14
说到现在,关于印度的林林总总,我能想到的都说了个梗概。当然印度还给予了我更多的体验,只是碍于暂时思维很难全部都概括一边,我主动很难完全讲出来。如果大家有问题,我会慢慢回答的。

最后说一点,关于印度,我很认可的一个优点:那就是印度的宣传能力
印度在文艺创作的水平上比中国高不少。
在印度,关于媒体文宣,我个人觉得最差的就是新闻类,完全是牛头不对马嘴。但是电视剧和广告,真的比中国强不少,特别是广告类。

我看过很多很多国内公司的宣传片,那水平可真是…劣质的3D动画,老总肥硕的头像,还有各种呆板的握手照做个Flash,要么就是一张接一张的资质证书…不管你的企业做得多好,但是宣传材料本身看过的观感,那就是可有可无!

这种视频做的,勉强可以称为视频资料,而不能称为宣传!宣传是要能打动人的,依靠强大的媒体冲击力,给人留下强烈的感官刺激,继而在第一印象上征服观众!反复的放一些用视频放诸如资质证书这样的静态画面,不仅从实用角度看很无用(投影一打,人家也看不清),而且让人很有感官疲劳感。特别是老总头像,握手等等,是我个人最反感的,而且都是用数码相机拍摄的,很粗制滥造的视频。我不客气的说啊,金砖四国中,就中国企业的宣传材料是最差的,从视频到宣传册,都乏善可陈,一点都不能掀起对方的兴趣。

特别是经过就是年代,大家都相信学好数理化,走遍天下都不怕。这话不错,但是不足,那就是:你只会做事,不会说话,更不会邀功!这让中国企业在走出去的时候吃了很多亏。我朋友在和一家瑞士的食品企业合作,他们的小团队一年只做出一点五个亿!是发展中国家里面做的最好的!但是年终评级,根本没他们的份!相反,获得总部表彰的一个印度团队,订单份额是他们零头都不到,还广受嘉奖!为何?很简单,不会邀功说话!
我那朋友的年报,写的中规中矩,全是报表,so,老外们拿去就大概看了看,都懒的细看!而欧美,包括拉美国家的报表,里面充满了各种图片,各种溢美之词,这里夸一下,那里捧一下。印度团队的报表,其煽动性之强烈,说词之感性,都让你觉得他们快要征服月球了!
虽然印度人业绩很差,但是他们通过媒体功力,强烈的煽动了阅读者的情绪,抵消了理性的判断力,造成了先入为主的印象,把很差的表现在人的思维中无限扩大化了!

(PS:大家可能会批判我,跨国公司的老总们怎么会这么二呢,怎么看报表只会看图片不会看数字呢?
我的看法是
1,他们很忙,和高考阅卷老师一样,花多少精力关注你的报告,全看他们的人品
2,报表就是用来忽悠人的。大家可以去下宝马,奔驰的年报看看,快速扫一眼,然后问自己,给你印象深刻的究竟是格式还是内容。然后再仔细的,慢慢的读完这些动辄百页的报告,然后问自己,究竟看到了什么有用的内容。
3,这些跨国高层中的很多,实际就是一群庸人,凡人,甚至昏人。古往今来,得高位者,有多少是靠真才实学?大家看中国历史就知道了,到底是明君多还是庸君多,是明相多还是庸相多?)

我们去的每一家印度公司,那宣传片放的,都让人觉得他们的事业是气吞山河,扫平八方。

即使这些年来,我国文化制品的设备水平有了提高,在创意上,还是一如既往的让人汗颜。
电视剧现在算是好点了,虽然雷作还是占主流。广告是最不行的,文字是平铺直叙,画面是没有冲击力,用语是以错别字为潮流,总之就是看了半天毫无亮点,还有最要命的是玩的太含蓄了让人不知道这公司是做啥的!至于羊羊羊之流,那是不提也罢。
这点上,我只能说印度人的广告制作的太好,好到我不觉得这种创意能力是来自一个发展中国家。大家如果有机会可以多看看印度的商业广告,不仅让你对产品本身瞬间来了兴趣,而且可以让你能领悟如何从一个独特的角度去阐述产品。

一个好的广告,可以不用一句旁白,不用一点字幕。我觉得国内很多搞创意山寨的,不用盯着欧美,翻墙去youtube上下载一些印度的广告,换个你的商标,立刻就能用。

印度人为什么这么擅长文宣?
我想这和印度人为什么这么不擅长基建是一个问题的两面:
原因就是印度人是高度感性化的!他们对色彩,声乐和节奏很敏感,对数字和逻辑很迟钝。在印度人表达事物的时刻,对心理的描述,对事物给当事人带来的感受,在重要性上大大超过对结果和过程在合理性上的要求。
这就是为什么印度电影充满了艳丽的色彩,轰隆而不间断的配乐,还有动辄就出现的无厘头舞蹈。这些夸张的演绎满足了印度观众对感性的追求,而其在理性层面的不合理早就被忽视了。

同样是使用英语的国家,我和同仁们现在可以很快分辨出印度人在文风上和英美国家英语使用者的不同
印度人在写作时,会着重描绘感情最强烈的部分:文章中充满了大量的形容词,对周边环境的详细性大大超过事物本身。
英美人在写作时,会着重描绘逻辑最重要的部分:文章中注意时间和地点关系,强调某年某月某时某人做了什么。

现在,我来模拟一下,如果是同一个商业案例,各种文化的人会怎么写

1,英国人:通常在西方文化看来,一个商人的宗教选择和他的商业策略选择是独立的,但是在印度,一个商人的宗教背景对他的商业规划有全面的,但难以量化的影响。

2,中国人:论宗教文化在商业规划中的重要性——在南亚次大陆,百分之85的人口信仰宗教,其中某一些宗教对人民生活和商业活动有巨大的影响,比如…………

3,美国人:约翰是一个在IT行业很有经验的成功商人,在他的判断中最近火爆的印度市场拥有很大的潜力。但是,约翰在美国的商业经验却遇到了印度当地的一个难题:宗教!下面我们具体说说约翰的故事……

4,印度人:如同每一个朦胧的早晨,在太阳还未从孟买的地平线出现时,哈桑已经起床。他望着自己还在熟睡的妻子,她美丽的脸庞在朝阳的映射下却如孟买湾的海水那样静谧,让哈桑不忍叫醒她,而是坐在床边独自沉思。他当年是村子里面最天才的孩子,是整个纳塔尔县第一个成为印度理工学院的骄子………省略一百字…………………哈桑和妻子举行了婚礼………省略一百字………他们终于在孟买,这个印度最昂贵的城市有了属于自己的公寓……省略一百字……然而,哈桑没有忘记自己的家乡,他的父母,他村中的长老,和他的信仰………省略一百字………这就是哈桑痛苦的选择,他不能背叛来自西方的主管,因为那样他将无法在孟买立足,不知届时他是否还能给予妻子和女儿如同现在一样的生活,然而他也不能选择背叛他的先知,因为…………省略一百字…………这就是很多印度当代商人遇到的问题,在宗教和商业间何去何从…………

最近看到印度人说自己的亩产水平,在不用农药和化肥的情况下,超过了中国袁隆平的杂交水稻!

作为涉及农业和环境行业的从业人员,我有必要说一句。这种已经超越了扯淡境界的印度新闻,本来根本就不值得老袁出来说两句,说了反而降低自己的身价。

我在印度期间也遇见了印度农业行业的同仁,以我和他们的接触,我不认为那些有责任感的印度农业同仁会说出这样的言论。这种烂说的言论,只可能出自嘴巴跑航天飞机的印度媒体之口

在化工,农业,环境,和物流背景的同仁看来,这个亩产超中国的言论实在可笑
1,印度和中国一样,大量依赖化肥和化学杀虫剂。这个世界上,不规模依赖化学药剂还能保持高亩产的农业技术,现在只在日本和少数西欧国家才。人家日本人是把做纳米芯片的精神用到种田上面去,把生态技术用到极致,才有摆脱化学农药的资本。
我不认为印度的农业科学和技术水平能达到日本的程度。

2,印度在耕作,灌溉,收割和仓储物流的水平上都很落后低效。如果你说你亩产万斤,请把土壤样本,灌溉设计,插秧,除草技术,轮耕时机,抑害手段等等都公布出来。现代农业早已超越了靠一亩良田一年好天气就能获得好收成的时代,没有生物技术和农科做保障,能出高亩产纯属玩笑。

印度有着大片的天然楝树群落,这是提炼强药力生物农药的原材料,有强效杀虫且减弱化学污染的潜力。但印度,很不幸,没有获得有效提炼这种生物制剂的技术,只能生产初级的乳化剂。从这点上看,印度的农业技术和对技术发展的投入,和中国不是在一个水平线上的。

印度的种种不靠谱,却在文艺上的绚丽多彩,都指出一个事实:印度人是高度感性的。

印度人的对感官的敏感不仅仅停留在视觉上,也体现在其他四感。在我们看来,印度影视中的舞蹈有些过于夸张,但此类桥段对印度人是无法缺少的。作为我们中国观众,看到这些桥段,我们印象最深刻的是印度人的动作,而对于印度人,他们最关注的却是我们看不见的——————音乐!是的,强烈的,夸张的,喧闹的音乐效果。印度的街头,电影和广告,时刻都有轰隆的音乐效果,没有音乐的生活对印度人简直无法忍受,这就好像我们四川同胞吃饭不放辣一样:活不下去呀!

艺术来自于生活,又高于生活。社会点滴就是音乐的土壤。如果在中国的街头听印度音乐,不会有人觉得会很合适吧,这是因为中国人的生活节奏不会产生类似的音乐,印度音乐用在中国人的文化场合,就会显得气场不合。但是,回到了主场作战,印度音乐和他们生活的节奏是相得益彰。当我们穿梭在孟买的街头巷尾,只有印度音乐夸张而快速的节奏,才能表达出我们当时的想法:一个忙碌而杂乱的社会,却在随机中有一种内在的秩序,恰似印度乐曲,在轰轰烈烈和跌跌撞撞中,又不偏不倚的演奏着相对的主旋律。

说道音乐和节奏,每个民族和文化都有自己特别的口味,不同的社会生活会演绎出不同的文化旋律。如果我们承认每个地区都是有文化差异的,那么每个地方的文化产品也必定是有文化差异的,因此一个地区的人不习惯另外一个地区的影视音乐是一个很正常的现象。从广义上来说,每一个文化都是平等的,不能认为印度神曲就比欧洲电音低级或差劲;狭义上来说,每一个地区内的人必定选择自己认同的文化,而疏远甚至斥责看不惯的文化。即使我们生活在一个全球化的世界,在很多文化层面上这个世界并没有趋同,而是在全球化的表面下发生着很多地区化的升级:一种国际化的标准平台被用作演绎新形式的地区文化。
比如MTV,这就是一个全球化的平台,中国人拍MTV,美国人拍,印度人拍,日本人拍,阿拉伯人也拍MTV,但没有人认为以上几个地区人拍出来的MTV是同一种产品。再比如电吉他,世界各地都有年轻在玩这个工业时代的乐器,即使在技术手法非常接近的背景下,各地的年轻人还是会不自觉的引入自己的文化,选择自己认可的演奏方式,如果有机会翻墙上youtube,大家听听日本人和美国人弹电吉他,再听拉美人的电吉他,三者间有很大的不同。

那有没有可能做出一个全世界各地人都能接受的文化产品呢?很难,但可以尝试,这就像做一道不甜不咸不辣不酸不苦的菜,也许大家都觉得略有欠缺不够味道,但又觉得和理想中的味道差的并不甚远,尚可一试。如此的原则,说起来简单,做起来很难,单是说众人的口味,印度人和中国人都说的辣,却是完全不同的体验,是不可被量化的感受。要做出跨全球可行的文化产品,需要大量的样本,在上万次的科学调研后,才能把握好尺度。这样的创作过程,是一种工业研发,是一种技术调试,和文化演绎已经想去甚远。

而什么样的文化产品是这样创作的呢。这样的例子还不少,来源就是美国。最好的例子就是迪斯尼动画电影的配乐,可以使用40多种语言在不同的国家播放,虽然每个地区的人都觉得不是很对味,但又都能接收!美女和野兽的主题曲,在美国可以冲榜,在英国可以冲榜,法语版本在法国可以冲榜,华语版本也能上榜,日语韩语印尼语泰语俄语西班牙语祖鲁语都可以在当地流传,如果仔细思考一下,这不是挺奇妙的一件事情吗?
剔除好莱坞的市场宣传优势,作为歌曲本身,它必须是能被当地人接受的,否则我们可以想象一下如果好莱坞使用的是一首纯正印度风的乐曲,即使美国人用尽宣传机器,在中国也不会广为流传。

现在有很多国家的文化事业,包括我们,也包括印度,都是以美国好莱坞为努力目标。但请允许我说,和好莱坞展开正面的对决是不可能的,因为我们的文化产品的创作流程还是脱胎于文化,而好莱坞是脱胎于工业性的研发和量产,这就像手工织布机和车床的竞争,毫无类比的可能和意义。但,如果我们选择好莱坞式的工业研发,完全以做一道普适性的菜肴为标准去开发我们的文化,我们又会面对丧失自己文化根基的尴尬。和工业革命不同,文化产业是每个国家社会文明的最后底线,有谁敢于以放弃自己根基的方式去进行一场好莱坞模式的现代化呢?不会

所以美国在这种程度上也是独一无二的,因为它以工业化模式去寻求结果最大化的道路上,没有太多的历史负担,没有太多的文化困惑。

简而言之吧
一个意大利人在熬制番茄酱时,他想到的会是:我要做出一个奶奶当年做的那样,味道很棒的番茄酱,我需要一些晒了几天的好番茄,那样大家都会喜欢而来买我的番茄酱的,

一个美国人在熬制番茄酱时,他的思维则是:我要去做一个能卖的出去的番茄酱,我需要做市场调研来定位,我需要一个意大利的成熟品牌,一个东海岸可靠的渠道,我需要知道整个东海岸的人都喜欢什么样的味道,而且我必须控制成本。

上面意大利人,是研发人员的思维,出于对产品本身的热爱
下面美国人,是销售人员的思维,出于对利润的热爱

你愿意选择哪一个呢?
如果你是电影导演,手头有一个花木兰的故事,你又会怎么选择呢?

印度人并非无能,但多数时候言过其实,被人看穿后印象大打折扣

我对地缘政治和核武器控制并不是很精通,所以只有两点简单的看法,
1,这年头,5大国怕的是有长程洲际导弹!光有了核弹,没有导弹技术发射,你只能在老家里自爆,或者派攻击机做载弹量和攻击距离都有限的空投。长程导弹太可怕了,基本上各国没有很好的防御措施。哪怕一枚常规弹头的导弹,打中了5大国的重要城市和军事据点,造成的军事和政治影响力可就不得了。美国现在怕朝鲜和伊朗,不仅仅是因为这两个国家耍流氓,搞核弹,更重要的是还要搞导弹!
反过来看,以色列这个基本算是默认的有核国家,因为没有长程载体,放核弹只会震慑阿拉伯国家,打不到欧洲和俄罗斯。但如果以色列有了长程导弹技术,俄罗斯和欧洲人绝对不会像现在这么平静,默认以色列拥核!

同理,因为大家都不认为印度的导弹技术是靠谱的,没有人认为印度有实际的核威慑力。印度的导弹只能伤到5大国中的中国,还只能打到我们的西藏和新疆,除了政治冲击外,不会对中国造成实际性的削弱。此外中国也让巴基斯坦拥核了,能大大的牵制印度,所以5大国都不认为印度有核弹是个很需要值得讨论的事情。

2,不拥核是5大国一致的态度,但印度这样的国家拥核了大家却也无所谓,毕竟给一只猫一把机枪它还是只猫。美国人执行的是实际主义外交,早不以意识形态和政治理想作为行动基准。我们说美国人是双重标准,事实是美国人是没有标准的,况且现在五大国都没有政治标准,不管是拉还是打,行事只有一个准则:在活命的基础上获利。活命是底线,在此之上为了获利从杀你全家到受胯下辱啥都做得出来。政治人物必须要不以物喜,不以己悲,不以道德伦理规则论英雄,只以结果评成败。在美国看来,反正印度伤不到美国任何要害,所以制裁印度毫无意义,而取消制裁可以给双方一个台阶下,留一个对话的后路,何乐而不为

在这之前,我想最后说一说关于印度发展模式的两个想法:

1,在发展工业的时候,应该如何处理农业?
一个残酷的事实是,在工业国家的草创之初,农业都受到了很大的压榨。远的案例有英国的圈地运动,近代的案例有乌克兰在苏联工业崛起中的牺牲,最近的则有刚刚离我们远去的三农问题。
想在一穷二白的基础上建立工业,作为一个国家的决策者,你要遇到以下几个挑战:1,资金在哪里?2,劳动力在哪里?3,土地在哪里?4,市场在哪里?
很显然,第二和第三点肯定要从农民的手上拿来。把农田转变为工业用地,依附于土地的农民自然也就成为了剩余劳动力,可以储备为未来的工业用人力。但是怎么转变农业用地呢?怎么把土地的所有权从农民手中交换到国家和资本家的手中呢?
人道的办法是征地补偿,和我们今天的拆迁安置一样。大家早谈判,早签字,早拿钱,早安心。
然后问题绕回了第一点:资金在哪里?这个问题应该用惊叹号而不是问号,所有的工业国家就和创业的老板们遇到的问题一样:万事俱备,但是没钱!
现在很多国家采用优惠政策,吸引外资来解决当地的资金问题,可这有两个限制:
1,量够不够?做个小打小敲的作坊可能够了,但是做个产业那差的太远。
我一个朋友在某城市的高新区负责引资,每年他的业绩要求是引资两亿,如果达到业绩就发引资量千分之一的奖金。这个听上去很美好的差事,我朋友干的是年年被扣工资,因为他拼死拼活也根本没法完成两亿的指标。大部分的外来投资都是百万级别的,能达到千万级别的一年也撞不到几个,就是撞到了也轮不到他接手。所以最后,他们引资办公室还是回到了传统的老路子:卖地!

2,谁来投资?你和投资者说你要建一个年产百万吨级的船坞,可现实是你的国家连火柴也不能生产,这是不是让人觉得你是个大忽悠?就算以后船坞能建立起来,也不一定保证造出来的船有市场呀!这样的投资,在大多数商人眼中绝对是肉包子打狗。
就像今天的银行一样,你越是缺资金,银行越不敢给你贷款。你越是不缺钱,银行却天天求你贷款。
在一个一穷二白的国度能够成功引资的,敢向一个一无所有的国家投资并获得成功的,都不能称为官员和商人了,而是神人!大家可以查查70年代末最早开始进入中国的外国企业是哪些,当时牵线搭桥的人都是谁,看看这些人的履历和生平,但绝不可刻意模仿他们的行为和思维,因为他们的眼光和魄力非常人能比。

好吧,既然如此,那牛奶只能出在牛身上,国家发展的启动资金还是要从农业里面扒出来!而且没有补偿的承诺
于是农业悲催了
它要向工业提供人力,提供启动资金,提供土地
而同时要用更少的土地,更少的人力和更少的土地,养活除自己外的产业工人!
这还不算完
如果第一批工业产品,因为种种原因卖不出去,在国际上没有市场,新兴的工人阶层也消化不了,农业人口还要负责为这批产品买单!

这种情况下,农民不闹才是奇怪的。

回顾历史,对于工业国家农民们在百十年前的悲惨遭遇,我们除了黯然流泪外,还要清醒的意识到,这是国家在转型的信号。
农业的痛苦孕育了工业,而20世纪末期工业的痛苦又孕育了什么产业呢?一场没落,总是对应着另一群新贵的崛起。比起“黯然流泪,念旧伤怀”。能快速换位站到“新贵”的队伍里,做一个“识时务”的俊才更重要,毕竟日子还要过,老婆还要娶,贷款还要还,哭是哭不出大洋的。

扯远了。回到印度。
这个国家最让我感慨的就是,它对它的农村有着很好的政策:比如,农村人全体不上税!
结合印度的其他社会现实,我不觉得它的农村在这四个层面上对国家的工业化发展有任何裨益。

1,资金:税都不收了,国家都制定保护农民的农产品价格了,还谈什么从农业抽血?

2,土地:谁敢动善良小伙子瓦奇纳的地,他在州议会的爷爷一定会振臂高呼的。

3,人力:有地的不需要离开土地,没有地的或者没文化,或者没路费,或者没有身份证……是的,大多数印度农村人没有身份证的概念,出了村子就算会讲话,也证明不了自己是某某某。想象一下在中国,如果你的店要招一个外来务工人员做帮厨,但他说不清楚自己到底是谁,这该有多诡异呀!想给他上医保都不知道怎么办。

4,市场:一部分人有买50辆奔驰的消费力,大部分人处于要吃饱的阶段,不知道何谓“消费”。

不过谁说农业就一定要去哺育工业的?谁说过农民就一定要勒紧裤腰带的?谁都有权利过好日子,过轻松点的日子,没有人愿意投胎下来就流血流汗过一生。

可是,如果你想快乐一点,就不要有太高的志向,因为也许你不能承担向高处攀爬的苦痛和恐惧。不论日后你是会变成武林高手,还是走火入魔的疯子,在进山门之前,你都要梯度落发,向以前田园牧歌的红尘说再见。可能在过程中你就这么默默无名的死了,比如阿根廷;亦或是壮烈自爆了,比如老毛子;或者活的上下里外焦虑烦躁不堪,比如日本;或者被宫了还要孜孜不倦,比如德国;或者活到了“会当临绝顶”,然后一览众山独自怜,比如×&(&)×(%¥¥#¥%——))(

是否走上工业化,城市化的道路,是一种选择,你可以不选。不过,如果你一边喝着女儿红,一边告诉别人说你在努力练金刚经,这就属于是笑话,是YY。。

对于印度的工业化选择,我上面的文字有点偏激了。印度作为一个国家,当然还是希望走上工业化道路的,但是它走的方式和我们想象的很不同。
印度的政治现实和文化背景,限制了它从农业中为工业化挖取第一桶金的可能。既然常规的道路走不通,打不扒对方,那就和对方做盟友吧。
在印度,农业不是工业化的盘剥对象,而是取悦对象,也就是说印度的工业化是以满足印度的农村需求为目标,而不是以现代西方工业国家的工业社会做为努力的方向。

这个怎么理解呢?
试想,我们要在印度农村建一个医院,建医院就要有医疗设备,有医疗设备就要用电,要用电就要建电站拉电线,而建电站就要占农村的土地……而印度农村的土地动不得,这怎么办?
A,常规工业化道路:圈地,建电站,拉电线,建医院,改善农村医疗条件……
这种方法从第一部就违背了农村的需求。很有可能,相比起每年去为了感冒挂水,农村还是觉得有一亩三分田比较重要,所以拒绝出让土地。A选项这种改善农村医疗的道路,其实是把农业社会的基础升级到工业社会,把农村的标准一步步拉到城市标准,说个通俗易懂的:就是社会改造

如果是以农村为尊重对象,完全以农村的利益出发,在不放弃土地的基础上解决医疗和用电矛盾,就得出了印度的策略:医疗设备要用电,那我就做不用拉电线的医疗设备,用电池和小发电机驱动,可能效果没有拉电线的大型设备好,但是没有占用农民的土地。
印度的工业化选择了这样不同于他人的道路,也就会受制于这样的道路,比如较少的购买力,较小的市场,较低的品质追求。

印度的许多产品在我们看来可能略带喜感,比如1000美元的TATA轿车,还有我前文说的小袋装洗发水,但这在印度的工业发展策略来看,是合情合理的严肃产品。
按照现代工业标准,这些产品可能质量很差,除了印度人外没有人会去买。但这些产品的初衷就没有理会过印度以外的市场,没有想过要和国际标准接轨。

我们从这点理解到,如果印度的工业化从来就不是以和国际市场接轨为目的,那么它就无法走亚洲四小龙的路。今天中国有很多过剩的技术和产业,不论从市场潜力还是从技术准备来说,这些产业转移的最佳方向都应该是印度。但现实是这些产业转移到了越南,到了印尼,到了老挝,最近有个趋势是向蒙古转移,全部都绕开了印度,盖因印度在硬件和软件上都主动推开了国际市场。

印度应该还是想融入这个体系,否则它为什么会在IT外包业做出那么大的投入,然而总体上印度却又没有做出融入这个体系的足够努力。国际市场有巨大的利润,但是融入国际市场的过程势必渐渐吞噬本土的需求,在心理上印度恐怕还有一个阴影那就是它非常害怕在拥抱国际市场的同时再一次变成百年前殖民下的加工厂和倾销地,于是,面对各种主观的和客观的“不能”,印度没有自信自己能够在巨大的冲击力下保住自己的“特色”。

当你选择发展一个产业时,是选择国内还是国外导向,是选择自立门户还是融入既有规则接人家的“尾单”,如果我选择国内那么我在什么时候什么条件下应该杀出国门走向更广阔的国际市场?如果我选择国外那么我应该有什么措施能保证一个以满足外国需要的平台能确实改善大多国内人的生活?
就是“走出国门”和“扩大内需”的轻重缓急愁死了许多的后进国家。
印度的痛苦就是它的内外向都走向了两个小
1,第二产业瞄准国内,市场太小,底子太小,跨不出国门
2,第三产业瞄准国外,从业人数太少,社会效益太小,不能改善社会大多数人的生活

以这两个命题做出发,研究西方7国历史上各行业“向外”和“向内”的时机和政策,是可以学到很多的。

我举一个具体的例子来解释下向内和向外的不同,这个例子其实应该在前面讲

大概7到8年以前,太阳能在中国兴起。我的一个老同学,弄了一批太阳能热水器,小赚了一笔。当时凡是安装了太阳能的家庭都觉得这个新玩意非常好:往楼顶上支起来,不管冬夏,只要有太阳,晒一个下午,晚上就有热水!这样天天洗菜洗澡都能用热水,特别是在冬天,真是舒服,还不用像以前那样付电费和煤气费!算一笔账下来,一个太阳能热水器用5到7年,比普通热水器省不少!

我的老同学发财不忘友人,就撮合他国外的一个朋友也进一批中国的太阳能热水器,到美国去卖。同样的产品,同样的性能,到了美国家庭后,反响非常的差:1,施工难,要领证,要改房屋供水管道,要扒房顶;2,支架脆弱,容易断裂,太危险;3,水不热

同样的东西,同样的用法,放在不同的环境,用户体验完全不同,这就是水土不服。
这个例子里面的太阳能热水器是一个“向内”的产品,能解决很多中国老百姓的需要,而且这些需要的解决从根本上改变了老百姓的生活品质:
1,以前没热水,现在有热水
2,以前热水要付电费和煤气费,现在不用付
1给你带来了新的服务,2为你节省了费用,何乐而不为。太阳能热水器这样的产品,在当时,用低的投入,带来给了顾客一个“从无到有”的方案,直接解决了“有没有”的问题。

但是这样一产品,到了美国,还能不能成为解决“有没有”问题的佳作呢?答案是不能
1,美国家庭普遍配有中小型锅炉,24小时供热
2,美国的电费和煤气费相对低廉,很多家庭是包月包年计价,没有作为一个很大的开销来考虑
美国的市场需要的是一个“好不好”的产品,而太阳能热水器在性价比上并不比热水锅炉改进很多,从用户体验上说大家并不觉得太阳能热水器在美国有很大优势。
既然优点看不明显,那么太阳能热水器在质量和操作难易度上的缺点就被相对放大了
1,美国住宅多为独栋民居,长期以来水电走线设计都是为布设锅炉考虑,所以主闸都在安装锅炉的地下室,砸开基墙接个管子上房顶再绕进室内确实很别扭。
2,大多数美国独栋民居的屋顶每4到6年要保养一次,每次保养屋顶要把热水器拆下来再装上去很折腾。
3,冬天有暴雪,夏天有大风,而独栋多为木质倾斜屋顶,屋顶内中空的保温层,支撑能力有限。一旦发生热水器倒塌,安全隐患很大

如果要想做美国的热水生意,在当时就应该做锅炉,而不是做太阳能热水器。

当然你会问,我们为自己的同胞们做热水器就好了,为什么要给大老远的美国人民做锅炉,这种东西在中国都没人用的。
答案是:太阳能一套卖6000人民币,锅炉一套卖6000美元。做太阳能你要拉单,采购,培训销售,找安装工,大概一套你能赚500到1000人民币;做锅炉,生产线会有美国人提供,材料由美国人安排,出货渠道由美方操心,你只要负责找一个厂房,拉上几个组装工,再拉一个和美国人做沟通的,质检上能符合美方要求,就行了,同样的时间段里你做一套能赚500美元,等于三千人民币。你选择做热水器还是做锅炉?
如果我再告诉你在国内经销商每次都赖款半年,而采购过来的材料上个批次好下个批次就能赖,而美方在季度开始就打来了七成的货款,你是选择做中国人的热水器还是做美国人的锅炉?

如果你一根筋的就是要做老百姓的热水器,放着多出来的两千人民币不赚,你是个傻子,还是个大傻子!狭义上说你自己的钱包瘪了,广义上说你不多赚一点,你能给员工开出更好的薪水吗?如果老百姓人人都和你的员工一样少赚了一点,他们有闲钱消费你的热水器吗?

如果你只做锅炉,认定了我要赚足这两千人民币,其他都不管,你又陷入了沉默。你发现虽然你有点闲钱了,但是国内却买不到实惠的热水器,因为压根没人做这个玩意!到最后为了有热水你还是必须去买美国人的热水器,虽然这玩意怎么用怎么别扭,怎么用怎么贵。

你在纠结到底是做热水器还是热水锅炉
还有人在纠结到底是做吉利还是做东风日产
也有人在纠结到底是做同仁堂还是做拜尔
另有人在纠结到底是做华为还是做朗讯

一面是短期内简单的快钱,但没有自主权,上升空间有限
一面是自主权,但起步维艰,没钱没市场只有包袱一个,上升空间则不知道,可能很大,也可能两三年死掉。

没钱活不下去,有钱很可能买不来自由,而活下去是实现自由的前提。那如何实现有钱又自由呢?放在今天往回看,每个人都能说出N多理论和总结

但在几十年前,没人知道答案。既然没人知道答案,那就“摸着石头过河”呗

最后,我下面借用那位在印度农村生活过8个月的达人在他QA帖子里面的问题,来详细介绍下印度吧

问:跟ZG比如何,印度的贫富差距貌似也很大
答:异常巨大!我觉得印度人的平均消费能力比我们低20倍左右……我参考了很多印度的收费,针对外国人的服务和本国人的服务,价格差异都会在20到25倍。比如景区,外国人门票500,本国人就是20到30卢比。吃饭,我们能接受的最低档的饭店是每人70卢比样子的消费,当地最差的是每人3到5卢比。火车,同样的距离,卖票的推荐我们买600卢比的二等舱位,当地人买的最多的是20到30卢比的仓位。
这还是平均数字,没说最穷的
但孟买市中心办公楼的租价堪比伦敦,企业高管拿得是英美标准的薪水,账面上看是我国同等级高管的10到15倍,当然我国有灰色收入,不过印度也有很多类似收入的。

问:印度人怎么看待raj
答:RAJ这个词在印度语里面意思是皇家的,高级的。我们认识的印度友人没有叫这名字的,都是比这复杂得多的。想来也是,我们中国也少有人会给自己起名张皇帝,刘豪华吧。在印度能看到的外国媒体不多,虽然印度奉行媒体自由,但是对本国的影视文化产业扶持很大。我在印度看过的电视剧和电影都是纯国产的,当然有很多剧情也是山寨好莱坞大片,这和我们这些年来常山寨日剧美剧一样。

问:印度英语水平如何?有多大比例学校用英语而非当地语授课?
答:印度的高等教育完全用英语授课,但印度人说的英语,发音和遣词造句上完全和欧美的英语不是一回事!我曾旁听一个印度朋友和餐厅的老板砍价,但是听了半天才反应过来他们说的是英语!大量的印度英语单词是彻底的原创,好多就是用英语拼写的印度语!如果在印度遇到一个说英语非常纯正的印度人,那说明他是在海外受的教育!越是高层,英语越纯正。

问:你同学家也有仆人吗?你看到的仆人都什么生存状态?
答:印度下层人的人工非常低贱!我前面说过,中产家庭可能买不起洗衣机,但是绝对能请得起佣人。至少我们拜会过的印度朋友,他们家里对佣人的态度都还是算仁厚的,这能从佣人的眼神观察出来。

问:那里的重男轻女真的很严重吗?
答:及其严重!印度妇女的地位可以类比我们民国时期,极少的几位有着宋氏三姐妹那样的社会地位,剩下大部分是绝对的男性附属。印度这个社会对女性也不友好。
印度女人很少出门,从低到高的工作大都是男的在干。印度的女生,要么不出门,出门肯定要拽上男人。除了宴会上有点经验的职业交际公关,很多印度女生在出去吃饭的时候都不愿意坐在陌生男士旁边。甚至有位青年女教授,为了陪我们参观,特别把她毫不相干的丈夫拉过来撑场!

问:农村底层人生活如此悲惨,那印度也有“农民工”吗?
答:有,但个人觉得印度农民工可以类比中国近代战乱时期的“流民”!他们一无固定工作,二无固定职业目标,三无固定居所,四无文化教育和社会保障。大多数是随人潮而来,在这里干几天,在那里干几天,有钱饿了就吃点,没钱累了就睡路边。我们同行的一位阿尔及利亚人觉得很不可思议,他说阿尔吉利亚也很穷,但没有人会让同胞睡大街上!印度人,不论民工自身,还是周围的人,对此都比较麻木。

这里顺便说一下,印度城市的下水道都不好,夏天雨季一来,城市内涝会淹死很多露宿的穷人!这些人死后也没有人收尸,是的,没有人觉得路边上有几个死人是个大问题!于是孟买这样的城市在雨季后因为尸体聚集就爆发瘟疫,大量贫民窟的人没被淹死也感染了瘟疫! 所以,有想去印度的,或者必须要去印度的,请避免雨季期间拜访印度大城市,去了也别靠近贫民窟地带,因为那里的状况可以套用生化危机里面的“红色感染区”,塞满了四处蹒跚的病原体!

问:你说的 印度贵族 是什么意思啊? 他们用什么营生?
答:请参考我前面对种姓和职业隔离的介绍。贵族在农村的权利,比在城市大得多。从什么时候播种收割,到调停争端,到红白喜事,决定权都在贵族。于是乎,你还可以问自己:中国古代的王爷和外戚们靠啥营生?

问:他们平时交流是印英杂合吗?你能听懂多少? 普通印度老百姓怎么看待中国和中国人? 他们的电视台有什么好玩的? 印度的种姓制度现在还有吗? 普通人唱歌好听吗?滑音和颤音的那种 印度人对待同性恋像Raj父母那样开明吗? 瑜伽他们练得怎么样?像我们普通人和功夫的关系吗? 他们家里供奉女王的画像吗?还有,你和印度人(男女喜好随便。。。)sexual intercourse过吗?
答:两个印度人在说话,我真不知道他们到底是在说英语还是印度语言!听起来都是一个调子的“呱唧呱唧咖咖哒哒”。我可以听懂,但需要耗费不少注意力。关于印度人对中国的印象,我个人认为自己没发言权,因为我们接触到的中上层更多,人越是高层,讲话越不得罪人,所以我不认为我听到的看到的能代表印度普通民众最内心的想法。印度电视台非常多,有三百个吧,内容挺多样化的,早就不是我们想象中全是跳舞唱歌那么千篇一律。大部分印度电视不使用英语,也没有字幕,节目以政论新闻和电视剧居多,选秀类娱乐节目很少。我追看了半个月一部印度电视剧,讲一个留学回来的年轻一代回到乡里以后如何继承家业的,看画面和人物表情觉得剧情挺直白的,没我们电视剧里那么多勾心斗角,也没美剧那么多逻辑陷阱。
印度人唱歌的不多,但都很善舞,跳起舞来可能动作不到位,但是和南美人一样很有激情,很有爆发力。
关于HOMO,我只能说印度男性之间物理距离实在太近,我们中国人会很不习惯。至于性关系,我没有听说过印度男性公开的例子,但周围确有印度人提醒我,我们这些长相白皙的外国男性也会被某些不能自己的份子盯上,这些人眼里没有男女区别,只要是能摸的能揉的活物都想插。
至于X行业,我们在印度没有耳闻目睹过!或者说这个行业在印度很隐蔽!印度人对男女之间的事情管控非常严格。我们发现,很多印度男大学生,20岁以上的,对X知识的了解都非常有限。和他们说黄段子,他们很纳闷,听不懂,反应不过来!不过这个要看人。有个接待我们的印度研究生,24岁,快结婚了,但是不知道怎么XX,还绕了半天向我们寻求这方面的知识!不过另一个男的,利用facebook约炮,隔三差五就去祸害小姑娘。facebook上印度人有私密的约炮群,和我们用qq一样,很多男女在上面发裸照,我必须说大多数妹纸的裸照,其色泽和体型,我们看了以后毫无兴趣%………

问:据说印度人不信任纸币,有钱人喜欢买黄金保值,楼主知道你同学家相关情况吗?
答:印度人对黄金有一种狂热。黄金在印度的资源进口中,仅次于石油,而且几乎全部流向个人饰品市场,少有工业需求。黄金投资是一种死投资,不会带动社会效益,但印度人就是喜欢投资黄金,不少人虽然家里很穷没钱存款,也要在墙角下挖个坑藏点黄金饰品。
印度人对黄金的认可,恐怕也是一种源自历史的无奈。在历史上,直到近代,印度都不是一个统一的国家,也当然没有全国统一的货币体系。如果没有统一的央行承担货币的发放,印度大地上长期就没有一种货币可以有信用全境通用!在没有纸币可以通用的情况下,使用贵金属是唯一可信的交易方式了!所以印度人要存黄金,因为长期无流通货币的历史让他们觉得存款啥都是没有信用的,只有自己手上的贵金属才是财富的保障。

你恐怕会问,印度人为什么不像我们以前的地主一样囤地呢?土地投资有一个前提,就是政治稳定!因为土地很难在短期内兑现,而且不像首饰一样可以带着随身走。印度在历史上政治稳定的时期也不多!经常互相内战,或者被外族侵略,所以印度人文化里,也不认为土地是很好的投资产品!

但是,土地对印度人还是很重要的财产!投资产品和财产的概念大家要分开认识哦!

印度是联邦国家,邦具体有哪些自治权?楼主见过印度某个级别的选举吗?
答:参见我前面对印度国情的描述。印度中央和地方邦的关系,可以类比我们的东周中央和列国的关系,你说自治权大不大?
没见过选举,但到处能看到很多选举广告,大街上贴的有,电视里广告轰炸也有。
PS:参见印度和台湾省,还有韩国,如果选举放开的话,拉选票在我国绝对是一个异常火爆的行业。

问:有什么好玩好吃的吗,景点收不收钱 ?
答:收钱。很多经典外国人价钱是对本国人收费的几十倍,但是服务好。
去印度,就不要去名胜。印度经典的估计维护,还有秩序维持,做的都不好。我们去了几个景区,大失所望,有那钱和闲心不如在中国玩徒步穿越。印度精彩的地方在于“体验”,前提是你胆子够大,忍受力一流,而且当地有靠得住的朋友。
好吃的吗,前面有很多介绍。在孟买我推荐的是吃烤鱼,其他一般般,味道和口感都欠佳。在印度别吃沙拉菜,麦当劳的也别,这些菜叶都不干净,也就是拿印度自来水冲了一遍,吃沙拉菜等于喝印度自来水。

问:问个重口味的:楼主在那里怎么擦屁股?
答:正规场所的厕所提供手纸。档次高的地方有自动冲PP功能的马桶,档次低的地方有水龙头+水桶,档次再低的地方&…………兄弟,还是憋着吧…那视觉冲击力会让你下面一紧,立刻忘却腹胀的烦恼…

问:你在印度时,印度主流媒体都关注过什么国际、国内问题?
答:我不知道什么是印度主流媒体。我在酒店能看到300个台,大概其中有三成是新闻频道,大到部长,小到非主流路人,谁都能在电视上貌似正经的放炮,所以我不知道哪一个是更有权威的电视台。印度新闻大多关注国内问题,把每个邦正在立案的腐败案件列举一下,新闻结束时间也差不多了

问:网上有好多印度人“开挂”的视频 就是非常不要命的站在火车上啦 什么什么的 印度人为什么那么不要命啊? 他们为什么那么胆大啊?
答:我们在印度的每天,在各种场合,都能见到各种“开挂”人物,特别是在城乡结合部地区。由于天天看,我们都麻木了。印度人不光挂火车,而且上下火车都是“跳车”,就和以前电视里面演的铁道游击队一样。开火车的也很配合,到站经常不完全停车,就是减速慢慢开,看跳下去和跳上来的人差不多了,一个加速冲出火车站……。
然后印度市内公车也是不停车的,上下车大家也是跳车。卡车也没有车门,司机堵车时就探出半个身子看看前方路况。
印度人是作秀狂,人来疯,看见你在拍照,五个人中只有两个会规矩站好,3个人会做出各种奇葩姿势。比如看见我在拍街景,就有个骑摩托车的青年冲我大喊,然后表演倒坐骑车。一个摊贩本来睡眼惺忪的,看到你掏出相机,立刻七手八脚,煎个鸡蛋都弄得和杂技一样。

问:他们的审美观念是怎么样的 是那种跟我们差不多的呢 还是说以黑为美什么的 (居然想半天想出了这样的问题- -
答:以白为美,以嫩为美,对面貌和身材的要求和世界主流接轨。长得白嫩,大长腿的妹纸到了印度会被围观……

问:听说印度的女孩子长得其实都不错 楼主你觉得呢?
答:印度的美女和凡人女子不在一个维度里。你在大街上能见到的,都是又黑又小,发育不良的。要看美女,就要去高档场合,比如我们去的宴会。或者是私人派对。

问:谢谢回答!又想到几个问题,他们吃的什么啊一般 有钱没钱的吃的相差大么 还有,最低一等的种姓是不是真的叫贱民啊- -以前听说两种不同等级的人如果在一起了 孩子就会被称为贱民,是最低的等级了,是么
答:相差的太大了。我们在酒店被宰,能吃出一顿每人两千卢比;大公司的高管食堂,每顿一人平均是150卢比;一般员工吃的是10卢比,中层吃的大概是40到60卢比。N多路人每天吃饭主要几卢比。
最低一等的种姓还不是贱民,贱民是没有种姓的……

问:哦。。这样的。。那贱民的父母强行突破种族界限在一起,自己会不会降级啊什么的- -
答:我想不出来父母双方如何突破种族界限。不同种姓出生不同,居住地不同,工作不同,上学的地方也不同,他们怎么有机会相见呢?

问:印度的消费水平和旅游水平如何? 比如说100块RMB在印度能买到些什么(普通的日常消费)
答:100人名币等于700到800卢比样子,只给一个人吃一顿饭的话,可以吃个不错的荤菜,比如大羊排+N瓶进口啤酒,N》4,Corona,喜力那样的,这在当地属于比较BT的消费了。去很好的酒吧,一晚上消费是4000卢比一人,人民币500样子。打车一般一程60卢比样子,也就是人民币10元不到。
印度的星级酒店一晚上要100美元左右,比较坑爹。除了住宿以外,如果不顿顿大吃大喝,不逛街扫货,我可以150块人民币在印度过一个星期。

问:两个人谈恋爱 难道还要互相问是什么种性啊?好奇怪噢
答:不用问的,能看出来,从相貌和名字都可以。
这个问题在印度我觉得就不是个问题
1,大多数人接触到的朋友,都是属于自己一个层次的。往上高层次的你没机会遇见,往下低层次的你心理上不屑于见
2,大部分印度人没有谈恋爱的概念。他们的婚姻由双方父母包办。

说道包办婚姻,我们不能以现实中国各种相亲悲剧来套入。作为一个子女,你应该相信父母在审美观和道德观上的造诣,那就是也许你父母不会给你选个天仙妹妹,但也不会很不济的给你找个超级大恐龙!毕竟人的审美观都是趋同的吗,何况你爹妈也有过年轻的时候。

问:楼主你说的这些情况在印度是少数的吧?印度不是相当民主的吗,肯定会有一些贱民努力读书,考了大学找到好工作,有了钱,那还是贱民吗?那些成功的贱民难道不可以取贵族吗?
答:印度的民主就是搞笑,选的人和被选的人都不明白民主为何物。
工作可以小幅度调整,收入可以有所增加,但是种姓是毕生的烙印,扔不掉带不走。我们古代时候商人也很有钱呀,但还不是受鄙视!?

问:对你这个外国人排斥吗?有什么好玩的事情吗?或是让你最难忘的事?
答:印度人对外国人的态度,就和我们刚刚改革开放的时候遇到外国人时一样,非常的好奇。我们走在大街上,就不停会有印度当地的人要和我们拍照。除了街头小贩推销东西的方式实在是太粘人以外,我觉得印度人对我们这些外国人十分友善。
不过,我还是要提醒女性同胞,或者男性同胞也包括吧,到印度的时候穿衣服要保守一点,不要暴露。在私人派对,公司晚宴上可以穿的招摇一点,但是走在大街上的时候,女生不要穿短袖短裙,男生不要穿短裤,不然当地人对用一种看到裸体人一般的目光盯上你。孟买的晚上并非罪恶城市,我曾多次晚上一个人在靠贫民区的地区行走,但请保持低调,不要穿的太显眼,不要背着很多东西炫富。当然,女生们晚上还是不要在外面走动了,三五成群也不安全,晚上要出行请酒店安排可信的出租车。个人认为德里的治安比孟买要乱。
最难忘记的事情是在印度喂乌鸦,是的,不是喂鸽子,是喂乌鸦,也许是喜鹊,但是我们分不出来,总之就是黑乎乎的,呀呀叫的鸟。那是在一家清真寺边上,我们逛完后出来买了很多羊肉窜,,一边吃一边就撒在地上喂盘旋在我们周围的乌鸦。话说,如果是在中国,一群人被近百只上下扑腾的乌鸦埋在中间,是个略显惊恐的事情,何况周围又是个垃圾堆,但不知道为何到了印度我们却觉得很淡然,恐怕是每天都能在大街上见到各种飞禽走兽的缘故吧。
也许印度的肉食对当地人来说真的很贵,他们都是用糙米喂鸟,所以像我们这样用肉喂鸟的可能真是罕见。于是,我们淡然的被一大圈乌鸦包裹着,外面又围 了一大圈印度人用手机拍我们。

哇,好可怕,你不会觉得惊恐么?你会阻拦这些类似的事情么?
答:印度人习惯任何人之间的距离靠的比较近,所以仆人侍奉你也会和你贴的很近。关于趴在胯下擦厕所的小黑人,我的体验已经在前面说过了。我不会阻拦这些事情,这是一个入乡随俗的问题,如果我阻拦了这些事情,让当事人丢掉了饭碗,岂不是把他们唯一生计的来源都断了?我也不会刻意的重复说谢谢,只是尽可能配合他们的工作,不让他们太辛苦,然后在他们长时间工作时和他们聊天解解闷,罢了。

问:印度人都穿什么?
答:我前面已经贴了很多照片。
我在孟买没有见过衣不遮体的人。大多数男士的着装和我们中国人一样,穿衬衫的占多,穿T恤的少点,但是甚少有穿牛仔裤的。8成大街上的女性穿莎丽。这个莎丽和我们的旗袍一样,也是分场合和档次的。有劳动妇女版本的莎丽,也有正式场合的莎丽,后者明显比前者华丽太多。粗糙的莎丽貌似就是颜色简单的几块布裹起来,华丽版本的莎丽我们有女同胞买了,做工非常精美,但配色大多太霸气,我们小巧的东亚女性难以镇住,一套很贵的有达到8000多人民币!莎丽对印度办公室女性来说也是正装,有领导视察,有贵客来访时,学校和办公室的女性就会换上莎丽,平时她们穿西式的变装,多为裤装,少有裙装。年轻点的女生会穿改良版的莎丽,是传统莎丽和现代西式服饰的结合体,类似我们现在很多民族风的设计。

问:印度人对中国人友好吗,对中国印象怎么样
答:和美国人对中国的理解一样,或者上述到周朝那样无所不知,或者处于白痴状态!不过印度人的白痴比美国人的那种自大型要好很多,交流起来氛围很好。但,同样,美国白痴的逻辑能力又比印度人要强,能从第一次见闻的事物中挑出逻辑左右来,呵呵。

问:据说印度人比较逆来顺受,不懂得反抗,导致历史上被其他名族征服过很多次,现在很多所谓的贱民,也许他们的祖先就是古时候的贵族,而现在所谓的贵族,可能都是故事,其他民族的征服者的后代,或是和当地人结合的后代。 这是真的吗???

答:征服印度的人太多。或者说,印度大陆上出现过,并列过的政权太多。早在亚历山大时期,印度就被西方来的征服者打败过,这导致了那时期印度的文艺出现了希腊风,还直接影响了佛祖雕像的造型。更早的就不知道了,因为征服印度的历史是由亚历山大记录下来的,印度本地人的历史一直依赖口述,逐渐淹没在历史传说里面,真假莫辩,时间不可推。印度的不少古迹说不清楚是谁建的,现代技术能推出年代,但没有历史日志却不知道那时那地是何人在当权。
今日印度的种姓制度也是个类似的糊涂账,你家祖上八代是谁,这大概还知道,但是再往前,你的家族从何而来,为何兴亡,就不晓得了。

问:哦,谢谢 再问一下 1,你所说的不同宗教的恐怖分子在印度国内是否猖獗,还是个别现象。 2,印度贱民是否有通过勤劳经商发财致富的机会,如果发财致富了,地位是否能得到改变。 3,大部分贱民都从心底承认自己生来卑贱吗?为什么?是否想过突破命运。 4,听说印度全民医疗保障,是否真的可以得…
答:孟买最近才被炸过……在此默哀一下,毕竟去过一座城市,即使没有感情也有记忆,希望我脑海中见闻的人们,没有当事罹难的。印度的贱民当然有通过经商发财的,有的小生意人做大了还能把自己的孩子送到国外去读书呢!孟买最大的一个食品加工厂就在一个贫民窟地区,在那里办厂的不可能是高等人吧,呵呵。
资本主义伟大的地方就是它把人的社会地位通过财富的方式和个人能力挂钩了,这在我们中国人看来有些微不足道,但是在世界很多地方包括当时的西方,这是个不小的进步!此前世袭的力量永远大于你的能力,你的财富,即使你功勋卓著家财万贯,你若不是贵族你依然受鄙视。反之,一个贵族即使是智障,穷到连内裤都当了,依然可以鄙视你,并随时可以夺走你的妻儿家产。为何资本主义强调私人财产神圣不可侵犯?就是因为当年的商人们和实业家的地位太低,被白痴的贵族和伪善的教士们侵犯的太惨了,所以他们要站起来对这些迂腐的上层阶级说不!印度今天依然有这样的问题,他身处资本主义的价值观洪流中,仰慕财富,又身处在传统文化的漩涡中,鄙视寒族,这就把有财富的寒族人士放在了一个尴尬的地位。当然,今天少人有会光天化日之下抢商人的钱财,但是致富后的下等人依然不能摆脱下等人的身份。你看看印度电影里很多时候商人要么是寡头一样的恶霸,要么是卑微的小人,而那些真正来自世袭文化体系的族长土司的后人们,却都是全能的英雄。

这个传统的力量有多强?即使一个下等印度人,有钱出国了,他的孩子们都在哈佛和牛津,出来后成了当地的名流了,他们回到印度依然是下等人……印度文化就是这么自我,很牛b吧。

问:他们上完厕所是用左手擦的吗?
答:孟买有点档次的厕所都是有手纸的。当然你要是联系的大部分人生活的话,恐怕是吧。

问:我有个老师是英印混血。,他爸去英国娶了个英国媳妇。。那他爸这种可以出国然后在外国结婚的属不属于贵族呀?
答:不属于。顽固的传统文化都反对混血和通婚,即使从生物角度上说很不合理,或者即使对方很优秀也是一个当地有名的人士,但传统就是反对通婚。君不见满清王后都是博尔济吉特,赫舍里和叶赫娜拉氏吗?君不见封建欧洲都通婚出白痴和血友病了还乐此不疲吗?家族婚姻就是这样,一开始是为了门当户对,再来是为了政治联姻,然后一旦形成了传统,就形而上学并难以改变了,即使对方家族的人选又丑又笨又臭脾气,即使对方家族政治失宠了财富流失了,你还是要娶这个嫁这个烂包袱。
你要违背了传统,就算找了个长相天资,才华出众,背景显赫,日进斗金的,其他人恐怕还免不了要嘟哝一下,说你不守规矩,但看得过去也就罢了。你要是找一个略有叛逆,平民子弟,那真算是大逆不道,要被逐出家门,抹去族谱。要是你和仇家的某某男女,演出了一部琼瑶剧,那怕是要家法伺候,千里索命呀!所以你看印度还有为了自由恋爱被凶杀的……

问:这么说来我们学校的那些印度交流生,学医的 都是婆罗门刹帝利咯? 看他们那么老土的样子,我还以为都是比较贫困低种姓的呢?
答:这个土气的感觉,恐怕是三种情况
1,人家不在乎外在的东西。乔布斯穿的就很土……
2,当地消费力有限,你觉得很土的玩意,在当地恐怕就是最好最新潮的了。话说80年代初期,我们出国留学的公派人员呀,都要作一套西服,夏天冬天都要穿。结果到了美国一看,走在大街上穿西服的感觉是最土的!同理,印度怕是也如此。ZARA在当地可是顶级品牌,你可想大多数印度人的穿衣消费相对我们而言是多“土”
3, 就是土,由内而外的。贵族又怎么样?住在农村的贵族也有呀,天天看的同样是九尺黄土一抹云,没见过外面的世界,土是肯定的。

问:印度社会乱吗? 还是很多人住在贫民窟?? 还有 印度人对外国人的态度怎么样?? 觉得印度好神秘=-=
答:态度问题前面说了。印度社会要说乱也是乱,不乱也算是不乱。对印度的社会治安,你不能抱太大希望,但也不能绝望,好歹比南美,非洲,中东那些整天有人往天上扫AK 的要好吧。印度的贫民窟我去过,那里倒不乱了,就是很恶心,就如同死水和河流的对比。流水不堵,而贫民窟就如同一汪黑幽幽的死水潭,死水潭怎么会乱呢?呵呵,它只会静静的冒着油乎乎的气泡。

我可以这么说,朋友们,如果你们能刚下飞机不经过任何适应,就去印度任何一个贫民窟,下车在里面走一百米,看着那景,闻着那味道,挤着那人,你能不起鸡皮疙瘩不反胃吐出来,我佩服你!

问:lz觉得可以用英文交流的典型的印度美女怎么样,脾气是不是比中国女孩好一些?如果是中国男人,大概事业需要多成功,才能娶到这样的女孩子? 如果和中国人结婚,孩子的种姓在印度是什么?她们自己的种姓会变化吗?
答:如果你遇到了一个印度美女,而且能用熟练的英文和你交谈,那么这个女人的背景肯定不凡,我恐怕估计这里的大多数人连和她举行婚礼仪式的钱都凑不出来……

问:但是中国男人应该没有欧美男人有市场,尤其正常交往的时候,应该是真实性情吧。LZ有没有想过找一个漂漂亮亮会英文的印度女孩结婚呢?
答:印度人不鼓励对外通婚的,当地大多数女性不敢也没想过要和老外结合。当然,家里已经移民到国外的印度人不在这里讨论。如果你有时间精力和语言能力,当然最重要是有情商和泡妞的经验,去泡印度美女不如直接去欧美国家找那里的小萝莉呢!皮肤白,说话嗲,平均综合水平绝对在印度女人之上。


Viewing all 764 articles
Browse latest View live